【算法】二分查找详解

22 篇文章 2 订阅

  二分是算法考察中的重要内容,通常用于在有序数组中进行查找,可以将时间复杂度从O(n)降至O(logn)。二分的思想十分简单,下面给出几种常见问题的模板写法。

1.二分查找

在递增数组中元素不重复,要求查找元素 x,查找成功返回 x 的下标,查找失败返回 -1。

public int binarySearch(int[] nums, int x) {
    int left = 0, right = nums.length - 1;
    while (left <= right) { // left == right 时也要判断一次
        // left + right 可能超过 int 的最大值导致溢出
        // Java 中有一种更好的写法是 mid = (left + right) >>> 1
        // 不管 left + right 是否溢出都能得到正确的答案
        int mid = (left + right) / 2;
        if (nums[mid] == x) {
            return mid;
        } else if (nums[mid] > x) {
            right = mid - 1;
        } else {
            left = mid + 1;
        }
    }
    return -1;
}
2.lowerBound

在递增数组中元素可能重复,要求找到第一个大于等于 x 的元素下标。

  1. 如果 nums[mid] >= x,说明第一个大于等于 x 的元素位置在 mid 或者 mid 左边
leftleft+1mid-1midmid+1right-1right
abcxxxxde
  1. 如果 nums[mid] < x,说明第一个大于等于 x 的元素位置在 mid 右边
leftleft+1mid-1midmid+1right-1right
abcdefxxg

于是可以得到如下的代码。一定要注意和上面 binarySearch 代码的区别!!!十分重要!!!

  • right = nums.length 而不是 上面的 nums.length - 1
  • 循环结束条件是 left < right 而不是 left <= right
public int lowerBound(int[] nums, int x) {
    // right 一定要初始化为 length 而不是 length - 1,因为查找的元素可能越界
    int left = 0, right = nums.length;
    // left == right 说明找到了唯一位置,就是结果
    // 所以与 binarySearch 不同,只需要 left < right 才循环
    while (left < right) {
        int mid = (left + right) / 2;
        if (nums[mid] >= x) {
            right = mid;
        } else {
            left = mid + 1;
        }
    }
    return left;
}
3.upperBound

在递增数组中元素可能重复,要求找到第一个大于 x 的元素下标。

  1. 如果 nums[mid] > x,说明第一个大于 x 的元素位置在 mid 或者 mid 左边
leftleft+1mid-1midmid+1right-1right
axxbcdefg
  1. 如果 nums[mid] <= x,说明第一个大于 x 的元素位置在 mid 右边
leftleft+1mid-1midmid+1right-1right
abcxxxxde

于是可以得到如下的代码。一定要注意和上面 binarySearch 代码的区别!!!十分重要!!!

  • right = nums.length 而不是 上面的 nums.length - 1
  • 循环结束条件是 left < right 而不是 left <= right
public int lowerBound(int[] nums, int x) {
    // right 一定要初始化为 length 而不是 length - 1,因为查找的元素可能越界
    int left = 0, right = nums.length;
    // left == right 说明找到了唯一位置,就是结果
    // 所以与 binarySearch 不同,只需要 left < right 才循环
    while (left < right) {
        int mid = (left + right) / 2;
        if (nums[mid] > x) {
            right = mid;
        } else {
            left = mid + 1;
        }
    }
    return left;
}
例题:力扣69.x的平方根[1]

给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。
示例 1:
  输入:x = 4
  输出:2
示例 2:
  输入:x = 8
  输出:2
  解释:8 的算术平方根是 2.82842…, 由于返回类型是整数,小数部分将被舍去。
提示:
  0 <= x <= 231 - 1

class Solution {
    public int mySqrt(int x) {
        int result = 0;
        int left = 0, right = x;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            // 防止溢出需要转为 long
            if ((long) mid * mid <= x) {
                result = mid;   // 只要小于x就能更新答案
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return result;
    }
}
Reference

[1]力扣69.x的平方根:https://leetcode-cn.com/problems/sqrtx

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值