顺序查找:
对于无序的情况:
什么是顺序查找:顺序查找的实现方式(存储),是含有两种的方式进行存储的,一种是顺序表的形式进行存储,还有一种是使用链表的形式进行存储的。采用顺序查找的方式是含有也是含有两种查找方式的:一个是从从头到尾进行查找,一种是从尾到头进行查找的!两种的效率都是差不多的,时间复杂度基本上是一致的。
两种代码实现的方式:
从头到尾:
//从头到尾的方式进行代码的查找实现
public static void sortHead(int [] arr , int key){int i;//左边的for (i = 0; i < arr.length ; i++){if(arr[i] == key) {break;}};System.out.println(i);
}
直接进行时间复杂度的计算的判断:
查找成功的时间复杂度为:
查找成功
$$
Pi*(n - i + 1)求和
$$
查找失败
$$
n+1
$$
从尾到头来进行查找操作
//从尾的情况来进行相应的查找的操作
public static void tailSort(int [] arr , int key){int j;for (j = arr.length - 1; j >= 0 ; j--) {if(arr[j] == key){break;}}System.out.println(j);
}
比较一下两者的查找效率,总体上来看得话,这两个的效率数量级是一致的。
这种的查找的时间效率非常的低,在数组的长度非常大的情况要进行对整个数组进行遍历操作,耗时的!这种是最简单的,没有对数组当中的元素含有任何的限制的,毫无顺序可言。但是对于单链表的形式进行存储的话,就只能使用这种方式一个一个进行的遍历操作。
对于有序的情况:
给定一个有序的数组,当想要对这个数组当中的元素进行查找情况(由小到大的顺序进行存储的)。
当查找的元素arr[i] 的位置的元素大小小于key的值,但是arr[i+1]的位置大小是大于key的值的。这说明整个这个数组当中都没有一个等于key的元素的,可以直接进行退出遍历操作了。
对于这种的有序的数组进行查找,含有n+1个不符合查找的情况的区间。当key的值落到了这个区间的话,就可以直接进行退出循环遍历操作了。
public static void getSort(int [] arr , int key){Arrays.sort(arr);int i;for( i = 0 ; i < arr.length; i++){if(arr[i] == key){System.out.println(i);break;}//数组越界i+1else if(i < arr.length - 1 && arr[i] < key && arr[i + 1] > key){System.out.println("找不到一个合适的");break;}}if(i == arr.length){System.out.println("找不到元素");}
}
上面是采用的是顺序查找的方式进行实现的。
有没有更好的方式来将代码进行更加妙实现的呢?
折半查找:
对一个有序的数组可以进行有序的查找操作,具体的实现流程是怎么样的?
给定一个数组(有序的),使用两个指针的[low , high],分别指向第一个位置的元素和最后一个位置的元素。然后不断的取这两个位置的中间值(mid),根据中间值的位置来进行判断操作[是将high移动(mid - 1),还是将low移动(mid + 1)].
以流程图的形式来将其进行展示!
代码实现:
package search;/*** @program: 数据结构* @description 二分查找* 二分查找是对有序的一串数组进行查找一个给定的数,若查找不到的话就返回一个查找不到的信息。* @author: YangTao* @create: 2023-07-28 10:34**/
public class BinarySearch {public static void main(String[] args) {int [] arr = new int[10];arr[0] = 1;for (int i = 1; i < arr.length; i++) {arr[i] = arr[i - 1] * 2;}for (int i = 0; i < arr.length; i++) {System.out.println(arr[i]);}
// 进行数据查找int number = 16;int left = 0;int right = arr.length - 1;int middle = 0;int index = -1;while(left <= right){middle = (left + right)/2;if(arr[middle] == number){index = middle;break;}if(arr[middle] < number){left++;}if(arr[middle] > number){right--;}}System.out.println("查找数得索引为:" + index);}
}
得到的节点连接的情况(判定树):
这样的判定树可以清晰的展示结果:处于哪一层就得要进行比较多少次!
得到的情况就是:
查找成功----
$$
(1+2*2+3*4+4*4)/ 11
$$
查找失败----
$$
(4 * 4 + 8 * 5)/ 12
$$
通过公式将整个的时间复杂度都求出来的情况:
查找成功的情况:
$$
1/n[1 + 2 * 2 + 3 * 4 + 4 * 8 + h * 2^(h - 1)]
$$
$$
((1 + n)/n) * log2(n + 1) - 1
$$
$$
log2(n + 1) - 1
$$
在处理mid的时候会出现两种情况:当使用的是向下取整的情况,在比较左右子树的大小的情况,左子树节点的个数可能是等于右子树的节点个数或者是为右子树节点个数减去1的情况。
但使用的是向上取整就会出现相反的情况:
右子树节点的个数可能是等于左子树的节点个数或者是为左子树节点个数减去1的情况。
这种查找的形式的二叉树二会是一个平衡二叉树。左右子树的高度差的不会超过1。
分块查找:
为数组建立一个索引表,并将数组分割成为若干个块,索引表中一次存放每个块内的最大值和块内元素的存储区间(块内无序,块间有序)
分块查找的过程:
-
在索引表中确定key所属的分块(索引表可以采用顺序查找,也可以采用折半查找)
-
在块内查找(非有序,只能顺序查找)
3.2.分块查找手算过程
3.2.1.顺序查找索引表
以查找36为例
-
在索引表中依次遍历找到第一个大于它的值,即40
-
移动到40指向的数组区间的第一个元素,即数组下标9
-
从数组下标9开始遍历,40→36 找到36
以查找17为例
-
在索引表中依次遍历找到第一个大于它的值,即20
-
移动到20指向的数组区间的第一个元素,即数组下标2
-
从数组下标2开始遍历,13→19→16→20
-
此时移动到20,数组下标为5,到索引表中17的最后一个元素,仍匹配失败,说明表中没有17
3.2.2.折半查找索引表
索引表只是保存元素的数组区间,方便在数组中寻找元素(相对位置),就算在索引表上找到相应元素,还是得再去数组中重新再找一次
以查找30为例
-
low指向10,high指向50,mid = (low + high) / 2 指向 2,即指向30
-
到30所指向的数组区间依次查找
以查找19为例
-
low指向10,high指向50→mid指向30,mid > key , high = mid - 1
-
low指向10,high指向20→mid指向10,mid < key, low = mid + 1
-
low指向20,high指向20→mid指向20,mid > key, high = mid - 1
-
low指向20,high指向10→low > high 折半查找结束
-
到low所指向的元素区间进行逐一遍历查找
去low指向区间查找的原因:最后low的左边一定小于key,而分块查找的索引表存放的是每块的最大值
除了能在key能在索引表上直接找到,否则都要执行到low > high,才能确定key元素所可能存在的数组区间范围
代码实现分块查找:
找到每一个块当中的最大的元素,同时记录每一个块的起始位置和结束位置。
package search;import java.util.Scanner;/*** @program: 数据结构* @description* @author: YangTao* @create: 2024-04-23 16:54**/
public class BlockSearch {public static void main(String[] args) {int [] arr = {9 , 22 , 12 , 14 , 35 , 42 , 44 , 38 , 48 , 60 , 58 , 47 , 78 , 80 , 77 , 82};//block的key的大小是属于逐渐递增的Block [] blocks = {new Block(22 , 0 , 3) , new Block(44 , 4 , 7) , new Block(60 , 8 , 11) , new Block(82 , 12 , 15)};System.out.println("输入查找的元素");Scanner scanner = new Scanner(System.in);int key = scanner.nextInt();//进行查找int index = search(blocks, arr, key);if(index == -1){System.out.println("没有找到");}elseSystem.out.println("key位于的位置是" + index);}public static int search(Block [] blocks , int [] arr , int key ){//对blocks的数组进行二分查找//当找到的元素是为key的话就可以直接获得这个位置的block的起始位置和结束位置。int low = 0;int high = blocks.length - 1;int mid = -1;int indexBlock = -1;while(low <= high){mid = (low + high) / 2;//找到了if(blocks[mid].key == key){//进行返回indexBlock = mid;break;}else if(blocks[mid].key > key){high = mid - 1;}else {low = mid + 1;}}//没有在主的索引位置找到相应的key值if(indexBlock == -1){indexBlock = low;}int index = -1;//在分块当中继续进行查找操作for(int i = blocks[indexBlock].low ; i <= blocks[indexBlock].high ; i++){if(arr[i] == key){index = i;break;}}return index;}
}//定义一个存储索引块表的类:
class Block{int key; //最大的节点的元素在块当中int low;int high;public Block(int key, int low, int high) {this.key = key;this.low = low;this.high = high;}
}