二分查找在顺序查找时时间复杂度为O(logn),是一种高效的查找算法
传统二分查找
public class BinarySearch {
public static void main(String[] args) {
int[] arr = new int[]{1, 2, 3, 4, 5};
int i = binarySearch(arr, 0);
System.out.println("i = " + i);
}
public static int binarySearch(int[] arr, int target) {
int left = 0;
int right = arr.length - 1;
while (left < right) {
int mid = (left + right) / 2;
if (arr[mid] > target) right = mid - 1;
else if (arr[mid] < target) left = mid + 1;
else return mid;
}
return -1;
}
}
存在的问题
二分查找的流程
- 找到数组中点的位置
- 判断待查找的元素在中点值的左面还是右面
- 在中点值的左面时就让右边界等于中点
- 在中点值的右面时就让左边界等于中点 + 1
- 重复此过程,直到待查找的值等于终点值时退出,否则返回-1
这里可以看到,由于查找时是从中点进行比较的,如果数组中的元素为1-1000时,待查找的值为10,此时再从中点开始查找时,二分查找的性能就远不如顺序查找来的快。
解决办法就是每次不是从中点进行查找,而是根据key来确定key右面的区间,从这个右区间进行查找。因此,就有一个确定这个区间右节点的公式
m i d = l e f t + k e y − a r r [ l e f t ] a r r [ r i g h t ] − k e y × ( r i g h t − l e f t ) mid = left + \frac{key - arr[left]}{arr[right] - key} \times (right - left) mid=left+arr[right]−keykey−arr[left]×(right−left)
此种查找方式又称之为插值查找,但是此种方式适用于查找的序列非常大,而且数据分布又比较均匀的情况。
优化后的代码
public class BinarySearchOptimize {
public static void main(String[] args) {
int[] arr = new int[]{1, 2, 3, 4, 5};
int i = binarySearch(arr, 2);
System.out.println("i = " + i);
}
public static int binarySearch(int[] arr, int key) {
int left = 0;
int right = arr.length - 1;
while(left < right) {
int mid = left + (key - arr[left]) / (arr[right] - key) * (right - left);
if(arr[mid] > key) {
right = mid;
} else if(arr[mid] < key) {
left = mid + 1;
} else {
return mid;
}
}
return -1;
}
}