在java中,我们常用的查找有四种:
- 顺序(线性)查找
- 二分查找/折半查找
- 插值查找
- 斐波那契查找
1、线性查找
线性查找是最简单也是最基本的查找了,其思想就是遍历数组中所有的值来与查找值相比较,找到了就返回。
2、二分查找
二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。
基本思路:首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。
时间复杂度:对数时间O(㏒₂n)
注意!!二分查找的数组中数据必须是顺序结构,也就是已经排好序的。
-
递归代码实现:
public static void main(String[] args) { int[] arr = {1,2,3,4,5,6,7,8,9,10}; System.out.println(find(arr,0,0,arr.length - 1)); } public static int find(int[] arr,int value,int left,int right) { int mid = (left + right) / 2; if(value > arr[mid] && mid < right) { return find(arr,value,mid + 1,right); }else if(value < arr[mid] && mid > left) { return find(arr,value,left,mid - 1); }else if(value == arr[mid]) { return mid; }else { return -1; } }
非递归代码实现:
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6,7,8,90,1000000};
binaryFind(arr,1000000);
}
public static void binaryFind(int[] arr,int val) {
int mid = (arr.length-1)/2;
int left = 0;
int right = arr.length - 1;
while(left <= right) {
if(arr[mid] > val) {
right = mid - 1;
mid = (left + right)/2;
}else if(arr[mid] == val) {
System.out.println(mid + "和" + arr[mid]);
break;
}else if(arr[mid] < val) {
left = mid + 1;
mid = (left + right)/2;
}
}
}
3.插值查找
- 插值查找算法类似于二分查找,不同的是插值查找每次从自适应mid处开始查找。
- 将折半查找中的求mid 索引的公式 , low 表示左边索引left, high表示右边索引right.key 就是前面二分里面的value
- 注意事项:对于数据量较大,关键字分布比较均匀的查找表来说,采用插值查找, 速度较快.
关键字分布不均匀的情况下,该方法不一定比折半查找要好
-
代码实现(和二分法基本一模一样,只是改了mid的赋值):
public static void main(String[] args) { int[] arr = {1,2,3,4,5,6,7,8,9,10000000}; System.out.println(find(arr,9,0,arr.length - 1)); } public static int find(int[] arr,int value,int left,int right) { int mid = left + ((value - arr[left])/(arr[right] - arr[left])) * (right - left); if(value > arr[mid] && mid < right) { return find(arr,value,mid + 1,right); }else if(value < arr[mid] && mid > left) { return find(arr,value,left,mid - 1); }else if(value == arr[mid]) { return mid; }else { return -1; } }
4.斐波那契(黄金分割法)查找算法
- 斐波那契查找原理与前两种相似,仅仅改变了中间结点(mid)的位置,mid不再是中间或插值得到,而是位于黄金分点附近,即mid=low+F(k-1)-1(F代表斐波那契数列),如下图所示:
对F(k-1)-1的理解:
由斐波那契数列 F[k]=F[k-1]+F[k-2] 的性质,可以得到 (F[k]-1)=(F[k-1]-1)+(F[k-2]-1)+1 。该式说明:只要顺序表的长度为F[k]-1,则可以将该表分成长度为F[k-1]-1和F[k-2]-1的两段,即如上图所示。从而中间位置为mid=low+F(k-1)-1类似的,每一子段也可以用相同的方式分割但顺序表长度n不一定刚好等于F[k]-1,所以需要将原来的顺序表长度n增加至F[k]-1。这里的k值只要能使得F[k]-1恰好大于或等于n即可,由以下代码得到,顺序表长度增加后,新增的位置(从n+1到F[k]-1位置),都赋为n位置的值即可。
-
代码实现:
static int maxSize = 20; public static void main(String[] args) { int[] arr = {1,2,3,4,5,6,7,8,9,10,11,12,13,14}; System.out.println(find(arr,14)); } //生成斐波那数列 public static int[] fib() { int[] f = new int[maxSize]; f[0] = 1; f[1] = 1; for(int i = 2;i < f.length;i++) { f[i] = f[i - 1] + f[i - 2]; } return f; } public static int find(int[] arr,int value) { int l = 0; int r = arr.length - 1; int k = 0; //斐波那数列的下标 int mid = 0; int[] f = fib(); while(r > f[k] - 1) { k++; } int[] temp = Arrays.copyOf(arr, f[k]); for(int i = r + 1;i < temp.length;i++) { temp[i] = arr[r]; } while(l <= r) { mid = l + f[k-1] - 1; if(value < temp[mid]) { r = mid - 1; k--; }else if(value > temp[mid]) { l = mid + 1; k -= 2; }else { if(mid <= r) { return mid; }else { return r; } } } return -1; }