查找算法种类
- 顺序查找
- 二分查找/折半查找
- 插值查找
- 斐波那契查找
在这里,顺序查找和普通的二分发查找不做概述。
插值查找
插值查找类似于二分法,不同的是插值查找就是每次自适应mid处查找。
理解关键程序
int mid = left + (right - left) * (val - arr[left]) / (arr[right] - arr[left]);
如上述一行程序:
left
:数组查询时最左边的索引
right - left
:数组查询时要查询的全部数字个数
(val - arr[left]) / (arr[right] - arr[left])
:其中val是要查找的值,因为插值查找类似二分法查找,因此查找的数组皆是按顺序排列的。(此处默认数组是按从小到大排序)。如果将(arr[right] - arr[left])
看成要查询部分的总长度,而``(val - arr[left])`可以看作查询的值距离左边的长度,将两者相除,估算出要查询值位于总数组的那一部分,进而估算mid。
可以如下图进行理解:

代码实现
public static int search(int[] arr, int left, int right, int val) {
if(left > right || val < arr[left] || val > arr[right]) {
return -1;
}
int mid = left + (right - left) * (val - arr[left]) / (arr[right] - arr[left]);
if(val < arr[mid]) {
return search(arr, left, mid - 1, val);
} else if (val > arr[mid]) {
return search(arr, mid + 1, right, val);
} else {
return mid;
}
}
斐波那契查找
斐波那契数列有一个特性:在斐波那契数列中,随这索引不断向后,数列中前一个值与后一个值的比会无限接近于黄金分割,即0.618
原理
根据查找数组的长度匹配已经生成的斐波那契数列,早到查询数组的黄金分割线,该线所在的位置是mid。其余与二分法类似(默认数组按从小到大排序)
mid的计算方法
斐波那契数列: fib[k] = fib[k - 1] + fib[k - 2]
假设fib[k]
是查询数组的长度,因为fib[k - 1] / fib[k]
随着k的无限增大,该公式无限接近于黄金分割,因此mid出现在fib[k - 1]
处。
fib[k] = fib[k - 1] + fib[k - 2]
转换为:fib[k] - 1 = (fib[k - 1] - 1) + (fib[k - 2] - 1) + 1
, 如果将fib[k] - 1
看作查询数组的长度,那么mid = low + fib[k - 1] - 1
算法图示

算法实现:
public static int search2(int[] arr, int val) {
int low = 0;
int high = arr.length - 1;
// 构建斐波那契数列
int[] fib = new int[20];
fib[0] = 1;
fib[1] = 1;
for(int i = 2; i < fib.length; i++) {
fib[i] = fib[i - 1] + fib[i - 2];
}
// 利用斐波那契数列,选取中值
int k = 0;
while(high > fib[k] - 1) {
k++;
}
// 填充0的部分
int[] temp = Arrays.copyOf(arr, fib[k]);
for(int i = high + 1; i < temp.length; i++) {
temp[i] = arr[high];
}
// 实现二分法
while(low <= high) {
int mid = low + fib[k - 1] - 1;
if(val < arr[mid]) {
high = mid - 1;
k--;
} else if(val > arr[mid]) {
low = mid + 1;
k -= 2;
} else {
if(mid > high) {
return high;
} else {
return mid;
}
}
}
return -1;
}