漫话算法:二分查找(带你刷题版)

129 篇文章 1 订阅
96 篇文章 0 订阅
本文介绍了二分查找算法在LeetCode中的应用,包括基本原理、处理溢出的技巧,以及在寻找元素、范围、平方根、插入位置、峰顶索引、峰值和特殊数组问题中的具体实现模板。强调了数组的二段性对算法执行的影响。
摘要由CSDN通过智能技术生成

二分查找是一种可以大大减少时间复杂度的算法,使用时数组需要有二段性(左右两侧不一样),那么,来刷题

1.704. 二分查找 - 力扣(LeetCode)

可以提炼模板的一道题,注意mid的算法防止溢出

class Solution {
public:
    int search(vector<int>& nums, int target) {
int left=0;
int right=nums.size()-1;
while(left<=right)
{
    int mid=left+(right-left)/2;
    if(target==nums[mid])
    {
        return mid;
    }
    else if(target>nums[mid])
{
    left=mid+1;
}
else{
    right=mid-1;
}
}

return -1;
    }
};

2.34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)

上一题我们得出了求中间节点的模板为

while(left<=right)
{
int mid=left+(right-left)/2;(防止溢出)
if()
{
left=mid+1;
}
else if()
{
right=mid-1;
}
else
{
}
}

现在我们来解决寻找左右节点的问题,首先,这道题由于是寻找最左侧和最右侧的节点,所以不能让left==right的情况进入循环,这样会导致死循环,所以条件为left<right,并且,如果存在寻找左右两个节点的情况,那么left+(right-left)/2和left+(right-left+1)/2就格外重要,这决定了mid是向左取整还是向右取整,我们的思路为查找某一侧的节点,就让另一侧节点移动,为了防止移动错过了目标节点,需要将其移动到mid位置,而结束条件为left与right相遇,这里相遇的关键在于让异侧的指针必须指向目标位置,所以要让异侧的指针想办法多走一步,所以找左左侧取整,找右右侧取整,以下为找左和右的模板

while(left<right)
{
int mid=left+(right-left)/2;
if()
{
left=mid+1;
}
else
{

}right=mid;
}
while(left<right)
{
int mid=left+(right-left+1)/2;
if()
{
left=mid;
}
else
{

}right=mid-1;
}

完整代码

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
    int left=0;
    int right=nums.size()-1;
    int begin=0;
    if(nums.size()==0)
    {
        return {-1,-1};
    }
while(left<right)
{
    int mid=left+(right-left)/2;
    if(nums[mid]<target)
    {
        left=mid+1;
    }
   else
    {
        right=mid;
    }
}
if(nums[left]!=target)
{
return {-1,-1};
}
else
{
begin=left;
}
right=nums.size()-1;
while(left<right)
{
    int mid=left+(right-left+1)/2;
    if(nums[mid]<=target)
    {
        left=mid;
    }
else
    {
        right=mid-1;
    }
}
return{begin,right};
    }
};

3.69. x 的平方根 - 力扣(LeetCode)

这是一个明显的找左节点的问题,这个题注意一点,就是注意模板中left+(right-left+1)与mid-1的绑定

class Solution {
public:
    int mySqrt(int x) {
        if (x == 0) return 0; 
    int left=1;
    int right=x;
    while(left<right)
    {
        long long mid=left+(right-left+1)/2;
        if(mid*mid>x)
        {
            right=mid-1;
        }
        else 
        {
            left=mid;
        }
     
    }
    return left;  
    }
};

4.35. 搜索插入位置 - 力扣(LeetCode)

这个题很明显是找右

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
int left=0;
int right=nums.size()-1;
if(nums[left]>=target)
{
    return 0;
}
while(left<right)
{
    int mid=left+(right-left+1)/2;
    if(nums[mid]<target)
    {
        left=mid;
    }
    else
    {
        right=mid-1;
    }
}
return right+1;
    }
};

5.852. 山脉数组的峰顶索引 - 力扣(LeetCode)

这个不是经典的有序数组,但是是一个二段性数组,这个找左或者找右模板都可以

class Solution {
public:
    int peakIndexInMountainArray(vector<int>& arr) {
int left=1;
int right=arr.size()-2;
while(left<right)
{
int mid=left+(right-left+1)/2;
if(arr[mid]>arr[mid-1])
{
    left=mid;
}
else
{
    right=mid-1;
}
}
return left;
    }
};

6.162. 寻找峰值 - 力扣(LeetCode)

这个二段性比较难发现,left与right是由峰顶的寻找移动的

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
int left=0;
int right=nums.size()-1;
while(left<right)
{
    int mid=left+(right-left)/2;
    if(nums[mid]>nums[mid+1])
    {
        right=mid;
    }
    else
    {
        left=mid+1;
    }
}
return left;
    }
};

7.153. 寻找旋转排序数组中的最小值 - 力扣(LeetCode)

这个题的重点在于关系不一定是相邻元素,也可能是其他关系,比如最后一个元素,这一点可以通过样例观察得出

class Solution {
public:
    int findMin(vector<int>& nums) {
int left=0;
int right=nums.size()-1;
int x=nums[right];
while(left<right)
{
    int mid=left+(right-left)/2;
    if(nums[mid]>x)
    {
        left=mid+1;
    }
    else
    {
        right=mid;
    }
}
return nums[right];
    }
};

8.268. 丢失的数字 - 力扣(LeetCode)

这道题要使用二分做也很简单

class Solution {
public:
    int missingNumber(vector<int>& nums) {
sort(nums.begin(),nums.end());
int left=0;
int right=nums.size()-1;
while(left<right)
{
    int mid=left+(right-left)/2;
    if(nums[mid]==mid)
    {
        left=mid+1;
    }
    else
    {
        right=mid;
    }
}
return nums[left]==left?left+1:left;
    }
};

9.来总结一下:使用二分要先判断数组是否具有二段性,如果具有二段性,则选择找左或者找右的模板,并且观察如果出现了-1则算中的时候+1

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值