1 算法简介
二分查找思路比较简单,对于一个有序数组,先比价目标元素和中间元素,如果比中间元素小则在左半部分查找;如果比中间元素大则在右半部分查找。
2 代码实现
public class BinarySearch {
public Integer search(Integer[] arrs, int num) {
return binarySearch(arrs, 0, arrs.length - 1, num);
}
private Integer binarySearch(Integer[] arrs, int left, int right, int num) {
if(left > right) {
return -1;
}
int mid = getMid(arrs, left, right, num);
if (mid > right) {
return -1;
}
if(arrs[mid] < num) {
return binarySearch(arrs, mid + 1, right, num);
}else if(arrs[mid] > num) {
return binarySearch(arrs, 0, mid - 1, num);
}else {
// 返回下标较小的元素
while (mid >= 0 && arrs[mid] == num) {
mid --;
}
return mid + 1;
}
}
protected int getMid(Integer[] arrs, int left, int right, int num) {
return (left + right) / 2;
}
}
3 算法优化
3.1 插值查找
上述方法选取mid下标的时候总是选择的中间的元素。如果我们查一个有序数组中最大的或者最小的那个元素需要多次折半才能查到。这里选取mid下标的时候做一个优化,根据目标元素和最大最小值关系来选取,代码如下
public class InterpolationSearch extends BinarySearch {
@Override
protected int getMid(Integer[] arrs, int left, int right, int num) {
return left + (num - arrs[left]) / (arrs[right] - arrs[left]) * (right - left);
}
}
3.2 斐波那契查找
普通二分查找是通过除法不断减半缩小搜索范围,这里我们用斐波那契数列来缩小范围。
举个例子,例如数组大小是 100,比它大的最小斐波那契数是 144,斐波那契数列如下:0 1 1 2 3 5 8 13 21 34 55 89 144
1)我们记 f(n) = 144,f(n-1) = 89, f(n-2) = 55。
2)选取比较值arrs[89],如果目标值比比较值小则在0-89范围查找;否则在89-100范围查找
3)如果在左边,令n = n-1;否则令n = n-2
4)递归上述过程即可完成查找。
斐波那契查找好处是用简单的加减运算代替了二分查找的除法运算,提高查找速度
代码实现
public class FibonacciSearch {
private int size = 46;
private int[] f = new int[size];
public FibonacciSearch() {
// 初始化fibonacci数列
f[0] = 0;
f[1] = 1;
for(int i = 2; i < size; i ++) {
f[i] = f[i - 1] + f[i - 2];
}
}
public Integer search(Integer[] arrs, int num) {
new BaseMergeSort().sort(arrs);
int k = 0;
// 在斐波那契数列找一个等于略大于查找表中元素个数的数F[k]
while (f[k] < arrs.length) {
k ++;
}
int lo = 0;
int hi = arrs.length - 1;
while (lo <= hi && k > 0) {
int mid = Math.min(lo + f[k - 1], arrs.length - 1);
if (arrs[mid] < num) {
// 在右侧查找
lo = mid + 1;
k -= 2;
}else if (arrs[mid] > num) {
// 在左侧查找
hi = mid - 1;
k --;
}else {
return mid;
}
}
return -1;
}
}