LeetCode笔记:二分查找/搜索模板

LeetCode笔记:二分查找/搜索模板

自大学开始,我便陆陆续续的学习一些 算法和数据结构 方面的内容,同时也开始在一些平台刷题,也会参加一些大大小小的算法竞赛。但是平时刷题缺少目的性、系统性,最终导致算法方面进步缓慢。最终,为了自己的未来,我决定开始在LeetCode上进行系统的学习和练习,同时将刷题的轨迹整理记录,分享出来与大家共勉。

二分查找/搜索

参考资料: 代码随想录

1、[left,right]

[left, right] (这个很重要)

这就决定了这个二分法的代码如何去写,大家看如下代码:

「大家要仔细看注释,思考为什么要写while(left <= right), 为什么要写right = middle - 1」

int searchInsert(int[] nums, int target) {
        int n = nums.length;
        int left = 0;
        int right = n - 1; // 定义target在左闭右闭的区间里,[left, right]

        while (left <= right) { // 当left==right,区间[left, right]依然有效
            int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
            if (nums[middle] > target) {
                right = middle - 1; // target 在左区间,所以[left, middle - 1]
            } else if (nums[middle] < target) {
                left = middle + 1; // target 在右区间,所以[middle + 1, right]
            } else { // nums[middle] == target
                return middle;
            }
        }

        return -1;
}

时间复杂度:O(logn)
空间复杂度:O(1)

2、[left,right)

如果说定义 target 是在一个在左闭右开的区间里,也就是 [left, right)

那么二分法的边界处理方式则截然不同。

不变量是[left, right)的区间,如下代码可以看出是如何在循环中坚持不变量的。

「大家要仔细看注释,思考为什么要写while (left < right), 为什么要写right = middle」

    int searchInsert(int[] nums, int target) {
        int n = nums.length;
        int left = 0;
        int right = n; // 定义target在左闭右开的区间里,[left, right)  target
        while (left < right) { // 因为left == right的时候,在[left, right)是无效的空间
            int middle = left + ((right - left) >> 1);
            if (nums[middle] > target) {
                right = middle; // target 在左区间,在[left, middle)中
            } else if (nums[middle] < target) {
                left = middle + 1; // target 在右区间,在 [middle+1, right)中
            } else { // nums[middle] == target
                return middle; // 数组中找到目标值的情况,直接返回下标
            }
        }

        return -1;
    }

时间复杂度:O(logn)
空间复杂度:O(1)


3、寻找边界的二分搜索

此模块参考:labuladong的算法小抄

⾄此, 你应该已经掌握了该算法的所有细节, 以及这样处理的原因。 但是, 这个算法存在局限性。

⽐如说给你有序数组 nums = [1,2,2,2,3] , target 为 2, 此算法返回的索引是 2, 没错。 但是如果我想得到 target 的左侧边界, 即索引 1, 或者我想得到 target 的右侧边界, 即索引 3, 这样的话此算法是⽆法处理的。

这样的需求很常⻅, 你也许会说, 找到⼀个 target, 然后向左或向右线性搜索不⾏吗? 可以, 但是不好, 因为这样难以保证⼆分查找对数级的复杂度了。我们后续的算法就来讨论这两种⼆分查找的算法。


寻找左侧边界的二分搜索

    int left_bound(int[] nums, int target) {
        int left = 0, right = nums.length - 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;
            }
        }
        //最后要检查 left 越界的情况
        if (left >= nums.length || nums[left] != target)
            return -1;
        return left;
    }

寻找右侧边界的二分搜索

    int right_bound(int[] nums, int target) {
        int left = 0, right = nums.length - 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;
            }
        }
        //最后要检查 right 越界的情况
        if (right < 0 || nums[right] != target)
            return -1;
        return right;
    }

补充: labuladong的算法小抄 的 二分查找模板

    int binary_search(int[] nums, int target) {
        int left = 0, right = nums.length - 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;
    }

作者:耿鬼不会笑
时间:2021年2月
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值