「这是我参与2022首次更文挑战的第27天,活动详情查看:2022首次更文挑战」。
前言
大家好,我是程序猿小白 GW_gw,很高兴能和大家一起学习进步。
以下内容部分来自于网络,如有侵权,请联系我删除,本文仅用于学习交流,不用作任何商业用途。
摘要
本文主要介绍普通二分查找以及普通二分查找的优化,能够使二分查找的应用场景增多。
二分查找
首先我们来学习普通的二分查找,举一个二分查找的栗子来学习。
给定一个有序数组和目标,返回目标在数组中的索引,如果不存在返回-1。
public int search(int[] nums, int target){
}
这里要说明的是给定的数组一定是有序的。
思想:
-
创建三个指针,将整个数组分为2个区间。[left,middle],(middle,right]
-
不断比较nums[middle]和目标的大小:
- 如果目标处于[left,middle]范围内,就更新右边界right和middle的值。
- 如果目标处于(middle,right]范围内,就更新左边界left和middle的值。
- 如果目标和nums[middle]相等,直接返回即可。
-
如果最后没找到就返回-1.
具体代码:
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length-1;
int middle ;
while(left<=right){
middle = left + (right - left) / 2;
if(nums[middle] == target){
return middle;
}
if(nums[middle]<target){
left = middle+1;
continue;
}
if(nums[middle]>target){
right = middle-1;
continue;
}
}
return -1;
}
二分查找拓展
我们来想一下,其实上面的代码是有bug的,如果我们遇到了有相同的值的数组,那么我们得到的结果可能会是其中一个随机的索引。接下来我们就对上面的代码进行优化一下。
可以看到,上面的代码时如果找到了就直接返回了,对于有重复值的数组来说这显然是不可控的,所以我们不能立即返回,而是应该不断缩小区间,从而找到重复值的第一个索引或最后一个索引。
代码优化:
int left = 0;
int right = nums.length;
while (left < right) {
// 注意 int middle = (left + right) / 2;
if (nums[middle] == target) {
right = middle; //不直接返回,不断缩小区间
}
else if (nums[middle] < target) {
left = middle + 1;
} else if (nums[middle] > target) {
right = middle;
}
}
// target 比所有数都大
if (left == nums.length) return -1;
return nums[left] == target ? left : -1;
小结
以上就是关于二分查找以及其拓展的一些思路和具体代码,希望能对读者有所帮助,如有不正之处,欢迎留言指正。
尽管二分查找效率已经很高了,但是我们还能对其进行进一步的优化(插值查找和斐波那契查找算法),使其效率更高,由于篇幅原因这里就不再讲述了,如果想要了解可自行百度。