Skip to content

二分搜索入门


基本介绍

应用场景

(1)在有序数组中确定 num 存在还是不存在

(2)在有序数组中找 >=num 的最左位置

(3)在有序数组中找 <=num 的最右位置

(4)二分搜索不一定发生在有序数组上(比如寻找峰值问题)

(5)“ 二分答案法 ” 这个非常重要的算法,后续学习

时间复杂度

如果数组长度为 n,那么二分搜索搜索次数是 log n 次,以 2 为底,二分搜索时间复杂度 O (log₂n)

二分搜索

题目链接

基本介绍

二分搜索是利用数组有序的特点,通过砍半来缩小搜索范围,优化了时间复杂度

代码实现

⚠️ 注意:以下代码写法区间为左闭右闭区间

java
class Solution1 {
    public int search(int[] nums, int target) {
        // 避免当 target 小于nums[0] nums[nums.length - 1] 时多次循环运算
        if(target < nums[0] || target > nums[nums.length - 1]){
            return -1;
        }
        int left = 0;
        int right = nums.length - 1;
        // 因为 left = right 在区间上有意义,所以取等
        while (left <= right) {
            // int mid = left + (right - left) >> 2;
            int mid = left + (right - left) / 2;
            /*
                中间值比目标值大,根据数组有序的特点,则右边的值不可能有比目标值小的元素,
                在左边找,更新右区间
            */
            if (nums[mid] > target) {
                right = mid - 1; // 由区间的定义知 mid 可以被取到,下一轮比较不希望再比较 mid
            /*
                中间值比目标值小,根据数组有序的特点,则左边的值不可能有比目标值大的元素,
                在右边找,更新左区间
            */
            } else if (nums[mid] < target) {
                left = mid + 1;
            } else { // 此时 num[mid] = target,返回下标
                return mid;
            }
        }
        return -1; // 没有找到
    }
}

>=num 的最左位置

初始值 anss = -1, L - R 范围上有一个中点

(1)中点 >= num,记答案,往左二分(num 右边的数肯定比中点还大,看看左边有没有 >= num 的最左数)

(2)中点 < num,不记答案,往右二分(num 左边的数肯定比中点小,中点右边的数才是大的,此时往右找 >= num 的最左数)

java
// 保证arr有序,才能用这个方法
// 有序数组中找>=num的最左位置
public static int findLeft(int[] arr, int num) {
    int l = 0, r = arr.length - 1, m = 0;
    int ans = -1;
    while (l <= r) {
        // m = (l + r) / 2;
        // m = l + (r - l) / 2;
        m = l + ((r - l) >> 1);
        if (arr[m] >= num) {
            ans = m;
            r = m - 1;
        } else {
            l = m + 1;
        }
    }
    return ans;
}

<=num 的最右位置

java
// 保证arr有序,才能用这个方法
// 有序数组中找<=num的最右位置
public static int findRight(int[] arr, int num) {
    int l = 0, r = arr.length - 1, m = 0;
    int ans = -1;
    while (l <= r) {
        m = l + ((r - l) >> 1);
        if (arr[m] <= num) {
            ans = m;
            l = m + 1;
        } else {
            r = m - 1;
        }
    }
    return ans;
}

⭐ 找峰值(数组不一定有序)

题目链接

https://leetcode.cn/problems/find-peak-element/

思路分析


题解

java
class Solution {
    public int findPeakElement(int[] nums) {
        if (nums.length == 1) {
            return 0;
        }
        // 检测左右边界是否是峰值
        if (nums[0] > nums[1]) {
            return 0;
        }
        if (nums[nums.length - 1] > nums[nums.length - 2]) {
            return nums.length - 1;
        }
        // 左右边界都不是峰值,在中间二分搜索
        int l = 1;
        int r = nums.length - 2;
        int ans = -1;
        // 左闭右闭:[l , r]
        while (l <= r) {
            // 取中点
            int mid = (l + r) / 2;
            // 如果中点左边的值比中点大,说明中点附近是下降趋势,往左边搜索
            if (nums[mid - 1] > nums[mid]) {
                r = mid - 1;
            } else if (nums[mid] < nums[mid + 1]) {
                // 中点附近是上升趋势,往右边搜索
                l = mid + 1;
            } else {
                // 中点左边、右边的值都比中点小,中点是峰值
                ans = mid;
                // 找到峰值,直接返回
                break;
            }
        }
        return ans;
    }
}