大家好,我是怒码少年小码。
什么是二分查找
二分查找(二分搜索),是一种高效的查找算法。它的使用前提是要在有序数组中使用。通过将目标值与列表的中间元素进行比较,从而缩小查找范围,直到找到目标值或确定目标值不存在。
二分查找的基本步骤如下:
- 设定查找范围的开始索引
low
和结束索引high
,初始时low = 0
,high = n-1
,其中n
是列表的大小。 - 计算中间索引
mid
,即mid = (low + high) / 2
。 - 将中间元素与目标值进行比较。如果中间元素等于目标值,则返回找到的元素/索引。
- 如果中间元素大于目标值,则将
high
更新为mid - 1
,即在左侧子列表中继续查找。 - 如果中间元素小于目标值,则将
low
更新为mid + 1
,即在右侧子列表中继续查找。 - 重复步骤 2 ~ 5,直到找到目标值或确定目标值不存在。
二分查找的时间复杂度为 O(log n),其中 n 是列表的大小。由于每次比较都将查找范围减半,因此它的查找效率非常高。
代码实现
循环方式实现
int binarySearch(int array[], int low, int high, int target) {
while (low <= high) {
int mid = (low + high) / 2;
if (array[mid] == target) {
return array[mid];
}else if(array[mid] > target) {
high = mid - 1;
}else {
low = mid + 1;
}
}
return 0;
}
如果你能写出以上的代码,那你大致达到了70分,还有一些地方需要优化。
在计算机中除的效率很低,因此我们把除法换成移位。也就是
把
int mid = (low + high) / 2;
换成:
int mid = (low + high) >> 1;
至此你就有了80分,还有哪里需要改进呢?当low和high很大的时候,low + high 也很大,可能会溢出。我们可以改成
int mid = low + ((high - low) >> 1);
high - low表示high和low之间的距离,>>表示除与2,再加上 low 就可以得到中间位置的索引。至于为啥(high - low) >> 1
之间要加括号,那是因为移位运算符的优先级小于加减号!!。最后改进版的如下:
int binarySearch01(int array[], int low, int high, int target) {
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (array[mid] == target) {
return array[mid];
}
else if (array[mid] > target) {
high = mid - 1;
}
else {
low = mid + 1;
}
}
return 0;
}
递归方式实现
如果能理解上面的方法,递归也就不难理解了。
int binarySearch02(int array[], int low, int high, int target) {
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (array[mid] == target) {
return array[mid];
}
else if (array[mid] > target) {
return binarySearch02(array, low, mid - 1, target);
}
else {
return binarySearch02(array, mid + 1, high, target);
}
}
return 0;
}
元素中有重复的二分查找
假如我们要找的元素在数组中有重复的,那么我们要返回的是最左边的那个。
显然这个问题的前提是我们已经找到了我们要找的元素,于是去检查它的左边有没有和他重复的元素,没有就返回它自己,有就一直往左走,一直到最左边的我们要找的元素
所以这个算法的其他分支和上面的相同,只有找到的if (array[mid] == target)
那条语句下方还要进行遍历和判断。
int binarySearch03(int array[], int low, int high, int target) {
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (array[mid] == target) {
return array[mid];
}
else if (array[mid] > target) {
high = mid - 1;
}
else {
//找到元素之后,接着往左找看看有没有重复的
while (mid != 0 && array[mid] == target) {
mid--;
}
if (mid == 0 && array[mid] == target) {
return mid;
}
return mid + 1;
}
}
return 0;
}
找到第一个不是目标元素的元素的时候while循环结束,此时的mid下标的元素是第一个不正确的元素,往后移动一个mid + 1
的下标的元素才是我们要找的元素,所以最后才return mid + 1;