概念
什么是二分查找?
二分查找,又称为折半查找,是一种在有序数组中查找指定目标的搜索算法。该算法通过将目标值与数组中间的元素进行比较来逐步缩小搜索范围,直到找到目标元素或发现其不存在为止。
基本模板分类
- 朴素二分查找
- 查找区间左端点
- 查找区间右端点
朴素二分查找
看下面的题目:. - 力扣(LeetCode)
对于这个题目首先想到的是暴力解法:从头开始遍历直到找到目标值停止;时间复杂度为O(n);
以[-1,0,3,5,9,12],目标值为9为例:
- 随便找一个数,如0:0<9,那么我们就可以缩小范围,只需要遍历:[3,5,9,12];
- 再找一个数 5,5<9,继续缩小范围,在[9,12]中遍历;
- 重复上面的步骤,直到找到目标值;
按照上面的查找方法,我们不一定非要取中间值,我们可以取1/3区间处的点,也可以取1/4,1/5,1/6......等任意区间,只不过在取中间位置的时候,效率会更快。
所以二分查找也就是:
- 找到中间节点,如果mid指针指到的数据小于目标值,更新left为mid+1;
- 如果mid指针指到的数据大于目标值,更新right为mid-1;
- mid指针指到的数据等于目标值,直接返回mid即可。
注意一下:
我们求中间节点的时候如果使用:(left+right)/2 当数据非常大的时候:left+right就会超出数据范围,因此我们可以用left+(right-left)/2 或者left+(right-left+1)/2 两种表示方法都可以;
class Solution {
public:
int search(vector<int>& nums, int target) {
int l=0,r=nums.size()-1;
while(l<=r)
{
int mid=(r-l)/2+l;//防止溢出
if(nums[mid]<target)
{
l=mid+1;
}
else if(nums[mid]>target)
{
r=mid-1;
}
else return mid;
}
return -1;
}
};
查找区间左端点
查找区间的左端点我们也和上面的方法一样:先看题目:. - 力扣(LeetCode)
以[ 5,7,7,8,8,10 ] ,目标值为8为例:我们要想找到左端点的8的下标,我们就要以划分成两个部分:【5,7,7】和【8,8,10】,第一部分都是小于目标值8的,第二部分都是大于等于目标值的;所以我们可以得到:
if(nums[mid]<target)l=mid+1;
if(nums[mid]>target)r=mid;
注意对于左指针的更新我们和朴素二分查找一样,但是右指针的更新发生了变化:
当我们的l和r分别指向如图的位置,如果在循环一次,l会指向8的左端点,r不变,要是r更新完mid-1,r所指的位置就不是我们要求的左端点的位置;
注意:
- 我们要注意一下循环条件:我们的l<r,这里不能写成l<=r;因为当l==r时我们的循环还会继续,l不满足小于目标值的条件,不会移动,r虽然满足大于等于目标值的条件,但是它还是等于mid,最后l=r,继续又会进入循环,从而造成死循环的局面;
- 还要注意一下中点的判断,在朴素二分查找中可以用left+(right-left)/2也可以用left+(right-left+1)/2;但是这里只能用left+(right-left)/2;如果我们用left+(right-left-1)/2就会造成死循环:其原因还是上面的那种情况,此时中点和r指向同一个,那么nums[mid]==traget,l不会移动,r也不会移动,l<r还是会进入循环,进入循环后mid还是和r指向同一个,l和r还是不会移动;
核心代码:
int l=0,r=nums.size()-1;
while(l<r)
{
int mid=(r-l)/2+l;
if(nums[mid]<target)l=mid+1;
else if(nums[mid]>=target)r=mid;
}
if(nums[l]!=target)......
查找区间右端点
求右端点和求左端点类似,我们就简短的说明一下:
还是以[ 5,7,7,8,8,10 ] ,目标值为8为例:我们要想找到右端点的8的下标,我们就要以划分成两个部分:【5,7,7,8,8】和【10】,第一部分都是小于等于目标值8的,第二部分都是大于目标值的;所以我们可以得到:
if(nums[mid]<=target)l=mid;
if(nums[mid]>target)r=mid-1;
核心代码:
l=0,r=nums.size()-1;
while(l<r)
{
int mid=l+(r-l+1)/2;
if(nums[mid]<=target)l=mid;
else if(nums[mid]>target)r=mid-1;
}
if(nums[l]!=target)......
哈哈 ,补充一下上面那个题目的AC代码
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
vector<int>f(2,0);
if(nums.size()==0)
{
f[0]=-1;
f[1]=-1;
return f;
}
int l=0,r=nums.size()-1;
while(l<r)
{
int mid=(r-l)/2+l;
if(nums[mid]<target)l=mid+1;
else if(nums[mid]>=target)r=mid;
}
if(nums[l]!=target)return{-1,-1};
else
f[0]=l;
r=nums.size()-1;
while(l<r)
{
int mid=l+(r-l+1)/2;
if(nums[mid]<=target)l=mid;
else if(nums[mid]>target)r=mid-1;
}
f[1]=l;
return f;
}
};
以上就是有关二分查找的全部内容,希望有所帮助!!