【Leetcode 专题】二分查找

框架套路

Leetcode 704. 二分查找
这类型的题是有框架的。需要记清楚的是while的判断条件和各个if情况下是否+1
在这里插入图片描述

第一种:最基本的二分查找算法

因为我们初始化 right = nums.length - 1
所以决定了我们的「搜索区间」是[left, right]
所以决定了 while (left <= right)
同时也决定了 left = mid+1right = mid-1

因为我们只需找到一个 target 的索引即可
所以当 nums[mid] == target 时可以立即返回

第二种:寻找左侧边界的二分查找

因为我们初始化 right = nums.length-1
所以决定了我们的「搜索区间」是 [left, right]
所以决定了 while (left <= right)
同时也决定了 left = mid + 1right = mid-1

因为我们需找到 target 的最左侧索引
所以当 nums[mid] == target不要立即返回
而要收紧右侧边界以锁定左侧边界,即right = mid - 1;

最后还要判断return nums[left] == target ? left : -1;

第三种:寻找右侧边界的二分查找

因为我们初始化 right = nums.length-1
所以决定了我们的「搜索区间」是 [left, right]
所以决定了 while (left <= right)
同时也决定了 left = mid + 1right = mid-1

因为我们需找到 target 的最右侧索引
所以当 nums[mid] == target 时不要立即返回
而要收紧左侧边界以锁定右侧边界

又因为收紧左侧边界时必须 left = mid + 1

最后还要判断return nums[right] == target ? right: -1;


对于我个人,我比较喜欢左右闭区间的写法

int binary_search(vector<int>& nums, int target) {
    int left = 0, right = nums.size() - 1; 
    while(left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1; 
        } else if(nums[mid] == target) {
            // 直接返回
            return mid;
        }
    }
    // 直接返回
    return -1;
}

int left_bound(vector<int>& nums, int target) {
    int left = 0, right = nums.size() - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1;
        } else if (nums[mid] == target) {
            // 别返回,锁定左侧边界
            right = mid - 1;
        }
    }
    // 判断 target 是否存在于 nums 中
    // 此时 target 比所有数都大,返回 -1
    if (left == nums.size()) return -1;
    // 判断一下 nums[left] 是不是 target
    return nums[left] == target ? left : -1;
}

int right_bound(vector<int>& nums, int target) {
    int left = 0, right = nums.size() - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1;
        } else if (nums[mid] == target) {
            // 别返回,锁定右侧边界
            left = mid + 1;
        }
    }
    // 此时 left - 1 索引越界
    if (left - 1 < 0) return -1;
    // 判断一下 nums[left] 是不是 target
    return nums[left - 1] == target ? (left - 1) : -1;
}

还有一种逆向思维的框架:
这个在153、154题会出现。

讲解:二分查找(Binary Search)合集

class Solution {
public:
    int searchInsert(vector<int>& nums, target) {
        int n = nums.size();
        int l = 0, r = n - 1;
        while(l < r) {	// 注意此时不能有等于了,因为我们这里是排除不可能区间
            int mid = (l+r)/2;
            if(nums[mid] < target)	// 这里不能判断等于的情况,因为我们用的排除思维,只需要排除目标一定不在的元素区间
                l = mid + 1;
            else r = mid;	// 这是nums[mid] >= target的情况,说明目标在mid及左边, 往左缩小
        }
        return nums[l] == target ? nums[l] : -1;	// 退出循环,要么找到,要么没找到,如果找到的话,left和right都指向它了
    }
};

完全有序

只要题目给出的数组是有序的,就要第一时间想到用二分法。

Leetcode 35. 搜索插入位置
Leetcode 34. 在排序数组中查找元素的第一个和最后一个位置
剑指 Offer 53 - I. 在排序数组中查找数字 I
剑指 Offer 53 - II. 0~n-1中缺失的数字
Leetcode 69. x 的平方根
Leetcode 367. 有效的完全平方数

不完全有序

旋转数组是部分有序,部分无序,也就是说确定了mid之后,mid的左边和右边是一个有序和一个无序的,所以我们确定了mid之后,应该马上确定mid的那一边是有序的,哪一边是无序的,再来确定下一个搜索空间(left和right的值)。

Leetcode 33. 搜索旋转排序数组
Leetcode 81. 搜索旋转排序数组 II
Leetcode 153. 寻找旋转排序数组中的最小值
Leetcode 154. 寻找旋转排序数组中的最小值 II

关于这4个题,反反复复做,人都给我绕晕了,我进行总结一下。
首先是前2题,while的条件为(l <= r),因为要找到target,所以必须在while中包含l==r的时候的情况;
然后是后2题,只能和右边界比较,while的条件为(l < r),因为我们并不需要找到某个确切的值,所以需要有出while的条件,即 l== r的时候出。
最后就是关于重复值的问题,154使用if(nums[r] == nums[mid]) r--;,81使用if(nums[l] == nums[mid] l++;
死记硬背了,嗐。

二维数组

Leetcode 74. 搜索二维矩阵
Leetcode 240. 搜索二维矩阵 II

其他

Leetcode 162. 寻找峰值
Leetcode 875. 爱吃香蕉的珂珂

Reference
【Leetcode 专题四】二分法

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值