3、二分查找(结合 LeetCode 69、34、35、81、154、540、4学习)(4待进阶)

作者学习算法的教材是LeetCode 101

有需要的同学直接GitHub搜索LeetCode 101即可

**

居合斩!二分查找

69. x的平方根(难度:简单)

  • 二分查找;
  • 代码如下
class Solution {
public:
    int mySqrt(int x) {
        int l = 0, r = x, ans = -1;
        while (l <= r) {
            int mid = l + (r - l) / 2;
            if ((long long)mid * mid <= x) {
                ans = mid;
                l = mid + 1;
            } else {
                r = mid - 1;
            }
        }
        return ans;
    }
};

34.在排序数组中查找元素的第一个和最后一个位置(难度:中等)

  • 二分查找;
  • 分别寻找左右边界;
  • 代码如下:
class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int leftBorder = getLeftBorder(nums, target);
        int rightBorder = getRightBorder(nums, target);
        // 情况一
        if (leftBorder == -2 || rightBorder == -2) return {-1, -1};
        // 情况三
        if (rightBorder - leftBorder > 1) return {leftBorder + 1, rightBorder - 1};
        // 情况二
        return {-1, -1};
    }
private:
     int getRightBorder(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        int rightBorder = -2; // 记录一下rightBorder没有被赋值的情况
        while (left <= right) {
            int middle = left + ((right - left) / 2);
            if (nums[middle] > target) {
                right = middle - 1;
            } else { // 寻找右边界,nums[middle] == target的时候更新left
                left = middle + 1;
                rightBorder = left;
            }
        }
        return rightBorder;
    }
    int getLeftBorder(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        int leftBorder = -2; // 记录一下leftBorder没有被赋值的情况
        while (left <= right) {
            int middle = left + ((right - left) / 2);
            if (nums[middle] >= target) { // 寻找左边界,nums[middle] == target的时候更新right
                right = middle - 1;
                leftBorder = right;
            } else {
                left = middle + 1;
            }
        }
        return leftBorder;
    }
};

35.搜索插入位置(难度:简单)

  • 二分查找;
  • 只需寻找左边界,然后结果加一即可;
  • 代码如下:
class Solution {
public:
    int getLeftBorder(vector<int>& nums,int target) {
        int l = 0,r = nums.size() - 1,mid,leftBorder = -1;
        while (l <= r) {
            mid = (l + r)/2;
            if (nums[mid] >= target) {
                r = mid - 1;
                leftBorder = r;
            }
            else {
                l = mid + 1;
            }
        }
        return leftBorder;
    }
    int searchInsert(vector<int>& nums, int target) {
        if (target > nums[nums.size()-1]) {
            return nums.size();
        }
        else if (target < nums[0]) {
            return 0;
        }
        else {
            return getLeftBorder(nums,target) + 1;
        }
    }
};

81.搜索旋转排序数组Ⅱ(难度:中等)

  • 二分查找;
  • 自己写的,排序之后正常二分查找(偷懒了偷懒了);
  • 代码如下:
class Solution {
public:
    bool search(vector<int>& nums, int target) {
        sort(nums.begin() , nums.end());
        int low = 0 , high = nums.size() - 1 , mid;
        while (low <= high) {
            mid = (low + high) / 2;
            if (nums[mid] > target) {
                high = mid - 1;
            }
            else if (nums[mid] < target) {
                low = mid + 1;
            }
            else if (nums[mid] == target) {
                return true;
            }
        }
        return false;
    }
};
  • 找出左右有序区间后二分查找;
  • 基本思想是,如果当前中点小于等于右端点,说明右侧有序,反之说明左侧有序;如果目标值在有序区间内,则对此区间二分查找,反之对另一半区间二分查找;如果遇到中点值等于左端点值,无法判断左侧有序还是右侧有序时,则将左端点值加一,排除一个干扰项(如10111 和 1110111101 );
  • 代码如下
class Solution {
public:
    bool search(vector<int>& nums, int target) {
        int low = 0 , end = nums.size() - 1;
        while (low <= end) {
            int mid = (low + end) / 2;
            if (nums[mid] == target) {
                return true;
            }
            if (nums[low] == nums[mid]) {
                low++;
            }
            else if (nums[mid] <= nums[end]) {
                target > nums[mid] && target <= nums[end] ? low = mid + 1 : end = mid - 1;
            }
            else {
                target >= nums[low] && target < nums[mid] ? end = mid - 1 : low = mid + 1;
            }
        }
        return false;
    }
};

154. 寻找旋转排序数组中的最小值 II(难度:困难)

  • 二分查找;
  • 首先是常规的二分查找,然后判断条件不同,若中点比右端点大,说明最小值在中点右侧,所以low = mid + 1,反之在左侧,high = mid,如果中点等于右端点,无法确定最小值在左侧还是右侧(如1111011),于是右端点前移,high - 1;
  • 代码如下:
class Solution {
public:
    int findMin(vector<int>& nums) {
        int low = 0 , high = nums.size() - 1 , mid;
        while (low <= high) {
            mid = (low + high) / 2;
            if (nums[mid] > nums[high]) {
                low = mid + 1;
            }
            else if (nums[mid] < nums[high]) {
                high = mid;
            }
            else {
                high--;
            }
        }
        return nums[low];
    }
};

540. 有序数组中的单一元素(难度:中等)

  • 思路简单,找到中点后判断是否出现两次,若不是,则返回,若是,判断除中点和与其相同的点外,左右两边哪边为单数,为单数的一边一定有单一元素;
  • 关键点在于只有三个元素时如何处理,只有三个元素时,判断中点和左右两个点哪个相同,返回不同点;
  • if (nums[mid] == nums[mid - 1]) return nums[high]; else return nums[low];
    该语句我本来也想变为三目运算符,提高简洁性,但是由于判断条件是==,不满足三目运算符的性质(表达式?表达式:表达式;),所以报错,只能改为if else形式(我理解的是==不属于表达式形式,如果是我理解有误,欢迎大佬指正指正);
  • 代码如下:
class Solution {
public:
    int singleNonDuplicate(vector<int>& nums) {
        if (nums.size() == 1) {
            return nums[0];
        }
        int low = 0 , high = nums.size()-1 , mid;
        while (low <= high) {
            mid = (low + high) / 2;
            if (nums[mid] != nums[mid + 1] && nums[mid] != nums[mid + -1]) {
                return nums[mid];
            }
            if (2 == (high - low)) {
                if (nums[mid] == nums[mid - 1])
                    return nums[high];
                else
                    return nums[low];
            }
            if (nums[mid] == nums[mid + 1]) {
                (high - (mid + 1)) % 2 == 0 ? high = mid - 1 : low = mid;
            }
            else if (nums[mid] == nums[mid - 1]) {
                (high - mid) % 2 == 0 ? high = mid : low = mid + 1;
            }
        }
        return 0;
    }
};

4. 寻找两个正序数组的中位数(难度:困难)

  • 归并排序后找中位数;
  • 代码如下:
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int i = 0;
        while (i < nums2.size()) {
            nums1.push_back(nums2[i]);
            i++;
        }
        sort(nums1.begin() , nums1.end());
        int low = 0 , high = nums1.size() - 1;
        int mid = (low + high) / 2;
        if ((low + high) % 2 == 0) {
            return nums1[mid];
        }
        else {
            double mid1 = nums1[mid];
            double mid2 = nums1[mid + 1];
            return (mid1 + mid2) / 2;
        }
        return 0;
    }
};
  • 进阶难度:时间复杂度为O(log(m+n))
  • 方法:找第k小元素
  • 代码如下:
在这里插入代码片
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值