二分查找二刷(35.搜索插入位置、 34.在排序数组中查找元素的第一个和最后一个位置、 69.x 的平方根、367.有效的完全平方数)

704.二分查找

题目链接:一刷总结

35.搜索插入位置

题目链接:Leetcode
文章讲解:代码随想录

解题思路

  这道题目的难点在于target不在数组中时,该如何返回正确的下标。如果直接用暴力循环,因为数组是升序的,因此只需要遍历到第一个nums[i]>=target的位置即为插入位置。
  使用二分法如何确定呢,先假设两个极端情况(这里使用左闭右闭的方式实现):
  1.如果数组中所有元素都大于target,那么left保持为0不变,right最终为-1
  2.如果数组中所有元素都小于target,那么right保持为n-1不变,left最终为n
可以发现,这两种情况中,最终left的值即为插入下标,这是不是巧合呢?实际上不是。给定一个任意升序数组,只要其中有一个元素的值大于target,就一定能找到一个区间[left, right]满足区间中的元素都大于target,这就回到了上面说的情况2。

解法
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int left = 0, 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 left;
    }
};
  • 时间复杂度:O(log n)
  • 空间复杂度:O(1)

34.在排序数组中查找元素的第一个和最后一个位置

题目链接:Leetcode
文章讲解:代码随想录

解题思路

  这里实际上就是怎么通过二分法确定元素区间的左边界和右边界,通过上一题的分析过程可知,如果target总是比nums[mid]小,那么right会一直左移而left不变,最终left即为插入位置。但是这道题会出现target==nums[mid]的情况,岂不是就不能让right 移动到预期的位置?实际上,可以调整条件为nums[mid]>=target,这样,就能按照刚刚的想法,使得right移动到预期的位置,这下目标元素区间的左边界就是最后得到的left了。
  那右边界呢,再来一次二分,将这个=的条件放到更新left的地方,这样就会是left移动到预期的位置,最后得到的right即为目标元素区间右边界。实际代码中,还需要注意一些边界条件的处理,看下方的解法可知。

解法
class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int n = nums.size();
        if(n<0)
            return {-1, -1};
        
        int left1 = 0, right1 = n-1;
        while(left1 <= right1) {
            int mid = left1 + (right1 - left1) / 2;
            if(nums[mid] >= target)
                right1 = mid - 1;
            else 
                left1 = mid + 1;
        }
        if(left1 > n-1 || nums[left1]!=target)
            return {-1, -1};

        int left2 = 0, right2 = n-1;
        while(left2 <= right2) {
            int mid = left2 + (right2 - left2) / 2;
            if(nums[mid] <= target)
                left2 = mid + 1;
            else 
                right2 = mid - 1;
        }
        return {left1, right2};
    }
};
  • 时间复杂度:O(log n)
  • 空间复杂度:O(1)

69.x 的平方根

题目链接:Leetcode

解题思路

  比较基础的一道题,不过其中还是有细节值得学习的。与前面的题目不同的是,这道题需要在二分的过程中记录期间计算的到的值,本题中即为mid。题目要求的是向下取整,因此在每次更新left的时候才进行mid的记录,同理,如果下次要求的是向上取整,在每次更新right的时候更新mid的记录即可。

解法
class Solution {
public:
    int mySqrt(int x) {
        if(x==0 || x==1)
            return x;
        long long left = 1, right = x;
        long long result;
        while(left < right) {
            long long mid = left + (right - left) / 2;
            long long square = mid * mid;
            if(square == x)
                return mid;
            else if(square > x)
                right = mid;
            else {
                result = mid;
                left = mid + 1;
            }
        }
        return result;
    }
};
  • 时间复杂度:O(log n)
  • 空间复杂度:O(1)

367.有效的完全平方数

题目链接:Leetcode

解法

class Solution {
public:
    int mySqrt(int x) {
        if(x==0 || x==1)
            return x;
        long long left = 1, right = x;
        long long result;
        while(left < right) {
            long long mid = left + (right - left) / 2;
            long long square = mid * mid;
            if(square == x)
                return mid;
            else if(square > x)
                right = mid;
            else {
                result = mid;
                left = mid + 1;
            }
        }
        return result;
    }
};
  • 时间复杂度:O(log n)
  • 空间复杂度:O(1)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值