二分查找入门

🔍 二分搜索算法详解 | [算法]-[中级]-[查找]

▶ JDK8+ | ⏱️ O(log n)

核心应用场景

  1. 基本二分搜索:有序数组中判断数字是否存在
  2. 左边界搜索:有序数组中找>=num的最左位置
  3. 右边界搜索:有序数组中找<=num的最右位置
  4. 非有序数组应用:寻找峰值问题(LeetCode 162)

算法实现

/**
 * 二分搜索详解 - 左程云算法讲解
 * 包含4个核心应用场景的Java实现
 */

/**
 * 1. 基本二分搜索:在有序数组中判断num是否存在
 * 时间复杂度:O(log n)
 */
class BasicBinarySearch {
    public boolean exists(int[] arr, int num) {
        if (arr == null || arr.length == 0) return false;
        
        int left = 0;
        int right = arr.length - 1;
        while (left <= right) { // [关键] 循环条件包含等号
            int mid = left + ((right - left) >> 1); // [优化] 防溢出写法
            
            if (arr[mid] == num) {
                return true;   // 找到目标值
            } else if (arr[mid] < num) {
                left = mid + 1; // 目标在右半区
            } else {
                right = mid - 1; // 目标在左半区
            }
        }
        return false; // 未找到
    }
}

/**
 * 2. 左边界搜索:在有序数组中找>=num的最左位置
 * 关键点:找到即记录位置,然后继续向左搜索
 */
class LeftmostSearch {
    public int findLeftmost(int[] arr, int num) {
        if (arr == null || arr.length == 0) return -1;
        
        int left = 0;
        int right = arr.length - 1;
        int index = -1; // [注意] 记录满足条件的位置
        
        while (left <= right) {
            int mid = left + ((right - left) >> 1);
            
            if (arr[mid] >= num) { // [核心] 大于等于条件
                index = mid;    // 记录当前满足条件的位置
                right = mid - 1; // [关键] 继续向左搜索更优解
            } else {
                left = mid + 1; // 向右扩大搜索范围
            }
        }
        return index; // 返回最左位置(未找到返回-1)
    }
}

/**
 * 3. 右边界搜索:在有序数组中找<=num的最右位置
 * 关键点:找到即记录位置,然后继续向右搜索
 */
class RightmostSearch {
    public int findRightmost(int[] arr, int num) {
        if (arr == null || arr.length == 0) return -1;
        
        int left = 0;
        int right = arr.length - 1;
        int index = -1; // [注意] 记录满足条件的位置
        
        while (left <= right) {
            int mid = left + ((right - left) >> 1);
            
            if (arr[mid] <= num) { // [核心] 小于等于条件
                index = mid;    // 记录当前满足条件的位置
                left = mid + 1; // [关键] 继续向右搜索更优解
            } else {
                right = mid - 1; // 向左缩小搜索范围
            }
        }
        return index; // 返回最右位置(未找到返回-1)
    }
}

/**
 * 4. 非有序数组应用:寻找峰值问题(LeetCode 162)
 * 关键点:利用相对位置决定搜索方向,不依赖全局有序
 * 时间复杂度:O(log n)
 * 
 * 问题要求:
 * - 峰值元素:严格大于相邻元素
 * - 相邻值不相等
 * - nums[-1] = nums[n] = 负无穷
 * - 可以返回任意峰值位置
 */
class FindPeakElement {
    public int findPeakElement(int[] nums) {
        int left = 0;
        int right = nums.length - 1;
        
        while (left < right) { // [注意] 用 < 而不是 <=
            int mid = left + ((right - left) >> 1);
            
            if (nums[mid] < nums[mid + 1]) {
                // [证明] 右侧存在更大值,峰值必在右侧
                left = mid + 1;
            } else {
                // [证明] 当前值可能为峰值或峰值在左侧
                right = mid;
            }
        }
        // 最终left==right,即为峰值位置
        return left;
    }
}

算法可视化

left=0, right=n-1
mid = left + (right-left)/2
arr[mid] == target
arr[mid] > target
arr[mid] < target
right = mid - 1
left = mid + 1
返回结果
left > right (未找到)
初始化
计算中点
目标判断
找到目标
左半区搜索
右半区搜索

算法要点说明

  1. 基本二分搜索

    • 循环条件:while(left <= right) ⚠️ 等号不可少
    • 本质是排除法(每轮排除一半不可能的区域)
  2. 边界搜索关键差异

    • 左边界:找到即左移右指针(记录可能更左的位置)
    • 右边界:找到即右移左指针(记录可能更右的位置)
  3. 峰值问题核心洞见

    证明:任意位置mid,至少有一侧存在峰值
    - 若 nums[mid] < nums[mid+1]:右侧必存在峰值
      (因为nums[n] = -∞,右侧终将下降)
    - 若 nums[mid] > nums[mid+1]:左侧必存在峰值
      (因为nums[-1] = -∞,左侧有上升过程)
    
  4. 复杂度分析

    • 数学基础:n → n/2 → n/4 → … → 1
    • 操作次数 k:n/(2ᵏ) ≈ 1 → k ≈ log₂n
    • 故时间复杂度 O(log n)
  5. 工程注意事项

    • 使用 left + ((right - left) >> 1) 防溢出
    • 峰值问题用 while(left < right) 避免死循环
    • 边界搜索的初始index值设为-1(未找到标识)

测试建议

  1. 基本搜索:测试存在/不存在/边界值情况
  2. 边界搜索:测试全小于/全大于/跨界情况
  3. 峰值搜索:测试单峰/双峰/单调递增递减情况

相关题目

🔗 基本二分:LeetCode 704 “二分查找”
🔗 左边界:LeetCode 35 “搜索插入位置”
🔗 右边界:LeetCode 34 “在排序数组中查找元素的第一个和最后一个位置”
🔗 峰值问题:LeetCode 162 “寻找峰值”

效率工具

二分查找模板生成

// 快速生成二分查找模板
public int binarySearch(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    while (left <= right) {
        int mid = left + ((right - left) >> 1);
        if (nums[mid] == target) return mid;
        else if (nums[mid] < target) left = mid + 1;
        else right = mid - 1;
    }
    return -1;
}

左老师的视频链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值