小小总结之二分查找三种情况

二分查找主要难点在于:

1. 起始条件 left,right的取值,一般若闭合区间,则0, length(nums)。如果涉及边界位置代入计算,则0,length(nums)。
2. 循环条件,第一要能够避免死循环,第二要看保留值(保留边界)问题,left <= right 无值保留,left < right则会剩下left = right一个位置,如果left + 1 < right 则会剩下left, right (right = left + 1)两个位置。
3. 向左向右查找区间:如果是需要保留当前mid作为边界则赋值为mid,否则相应进行+1、-1操作;另外注意避免死循环问题即可,根据具体来定是否进行+1、-1操作。



First:

原题链接:

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

image-20240404202751823

int search(int* nums, int numsSize, int target) {
    int left = 0, right = numsSize - 1;
    while (left <= right) {
        int mid = left + ((right - left) >> 1);
        if (nums[mid] == target) {
            while (mid > 0 && nums[mid-1] == target) {
                mid--;
            }
            return mid;
        } else if (nums[mid] > target) {
            right = mid - 1;
        } else {
            left = mid + 1;
        }
    }
    return -1;
}

简单的题,题目直接告诉二分查找,并且让搜索是否存在固定值,即:等于x的下标这一情况,直接经典模板,三种比较结果分开列。

边界问题:在查找到目标值时,我们需要进行简单的处理:目标值是否重复?如果不确定的情况下,我们就需要进行查找:

        if (nums[mid] == target) {
            while (mid > 0 && nums[mid-1] == target) {
                mid--;
            }
            return mid;

while循环的条件设置为:边界+判断数组的值是否与目标相同,内部为下标左移或右移,当查找到了,就return下标的值即可。



Second:

原题链接:

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

image-20240404202821100

int searchInsert(int* nums, int numsSize, int target) {
    int left=0,right=numsSize-1;
    int ans=numsSize;
    while(left<=right){
        int mid=left+((right-left)>>1);
        if(nums[mid]<target){
            left=mid+1;
        }
        else {
            ans=mid;
            right=mid-1;
        }
    }
    return ans;
}

返回按顺序插入的位置,即:大于等于x的最小下标。

我们这个时候就可以想想,

当左边界收缩的时候,对我们的结果有影响吗?

没有,因为我们要求的是最大的下标,那么我们只有在右边界收缩的时候才会影响我们的结果,

所以我们的用一个答案变量保存一下我们的mid,再进行收缩,

那么当右边界收缩的时候,mid的结果大于目标,所以就保证了这个数据一定大于等于我们目标值,插入的时候就需要插入到这里。我们返回答案变量即可。

为什么继续查找分别left需要+1,而right不再-1

从起始条件可以看出,每次二分查找的区间为[left, right),即区间左闭右开。
因此在target与nums[mid]进行比较时,左侧区间和右侧区间分别改变为了[left, mid)和[mid + 1, right),同样是确保每次mid处的元素不会重复拿出来进行比较(防止死循环)。



Third:

原题链接:

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

image-20240404202837711

int mySqrt(int x) {
    long long left = 0, right = 10000000;
    long long ans = 0;
    while (left <= right) {
        long long mid = left + ((right - left) >> 1);
        if (mid * mid <= x) {
            ans = mid;
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return (int)ans;
}

这里需要我们理解一下,找平方根,并且保留整数部分,什么意思呢,也就是结果必定小于等于真实的情况,也就是:小于等于x的最大下标。

我们在这里,返回的是平方根,所以我们直接让mid的积<=x就可以啦,我们回顾一下上一题,也就是说我们需要返回的是左边界的收缩前的结果。



Fourth:

原题链接:

367. 有效的完全平方数 - 力扣(LeetCode)

image-20240404202851808

bool isPerfectSquare(int num) {
    long long left=0,right=num;
    while(left<=right){
        long long mid=left+((right-left)>>1);
        if(mid*mid<num){
            left=mid+1;
        }
        else if(mid*mid>num){
            right=mid-1;
        }
        else return 1;
    }
    return 0;
}

返回是否存在,也是让我们进行查找,如果查找到了那就是存在,未查找到那就是不存在,理论上是第一种情况。

但是我在尝试的时候发现:

这个题可以用上面三号的代码,

也可以将<=变成小于然后再将ans放在right区;

或者就如这里所示,直接分开三个区段。(即判断的应该的类型)

这里发现他可以用三种情况的代码来实现,有点不知道为什么。

目前对于三种方法理解: = 在哪个区间,就 r u t u r n 哪里的值(根据题意改变)。 目前对于三种方法理解:=在哪个区间,就ruturn哪里的值(根据题意改变)。 目前对于三种方法理解:=在哪个区间,就ruturn哪里的值(根据题意改变)。

  • 30
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值