跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava算法

Java 二分查找算法实战:从基础到进阶

二分查找利用有序数组的二段性,将时间复杂度优化至 O(log N)。通过七个经典例题,涵盖标准查找、边界定位、平方根计算、插入位置及峰值寻找等场景。重点讲解如何根据单调性调整左右指针边界,处理溢出风险,并对比了哈希、遍历等其他解法在特定问题中的优劣,帮助读者掌握二分法的变体应用。

极光发布于 2026/3/15更新于 2026/10/2595 浏览
Java 二分查找算法实战:从基础到进阶

二分算法核心思路

二分查找的核心在于利用数据的二段性。在一个有序数组中,如果某个条件成立,其左侧(或右侧)必然满足某种规律,从而可以舍弃一半空间。相比暴力遍历的 O(N),二分能将复杂度降至 O(log N)。

1. 标准二分查找

在有序数组中查找目标值 target,找到返回下标,否则返回 -1。

class Solution {
    public int search(int[] nums, int target) {
        // left 和 right 分别指向当前搜索区间的两端
        int left = 0;
        int right = nums.length - 1;
        
        while (left <= right) {
            // 防止整数溢出
            int mid = left + (right - left) / 2;
            
            if (target > nums[mid]) {
                // 目标在右半部分 [mid+1, right]
                left = mid + 1;
            } else if (target < nums[mid]) {
                // 目标在左半部分 [left, mid-1]
                right = mid - 1;
            } else {
                return mid;
            }
        }
        return -1;
    }
}

注意:循环条件是 left <= right,因为当 left == right 时区间内仍有一个元素需要检查。

2. 在排序数组中查找元素的第一个和最后一个位置

题目要求找到目标值的起始和结束下标。由于存在重复元素,不能直接套用标准二分,需要分别寻找左边界和右边界。

  {
     [] searchRange([] nums,  target) {
        [] ret =  [];
        ret[] = ret[] = -;
         (nums.length == ) {
             ret;
        }
        
        
           ;
           nums.length - ;
         (left < right) {
               left + (right - left) / ;
             (nums[mid] < target) {
                left = mid + ;
            }  {
                right = mid;
            }
        }
         (nums[left] != target) {
             ret;
        }
        ret[] = left;
        
        
        left = ;
        right = nums.length - ;
         (left < right) {
            
               left + (right - left + ) / ;
             (nums[mid] > target) {
                right = mid - ;
            }  {
                left = mid;
            }
        }
        ret[] = right;
         ret;
    }
}
class
Solution
public
int
int
int
int
new
int
2
0
1
1
if
0
return
// 寻找左边界
int
left
=
0
int
right
=
1
while
int
mid
=
2
if
1
else
if
return
0
// 寻找右边界
0
1
while
// 向上取整,避免死循环
int
mid
=
1
2
if
1
else
1
return

3. x 的平方根

求 sqrt(x) 的整数部分。这属于'答案具有单调性'的场景,可以在 [0, x] 范围内进行二分。

class Solution {
    public int mySqrt(int x) {
        if (x < 1) {
            return 0;
        }
        // 使用 long 防止乘法溢出
        long left = 1;
        long right = x;
        while (left < right) {
            long mid = left + (right - left + 1) / 2;
            if (mid * mid <= x) {
                left = mid;
            } else {
                right = mid - 1;
            }
        }
        return (int) left;
    }
}

4. 搜索插入位置

在升序数组中找到目标值的位置,若不存在则返回应插入的位置。本质是寻找第一个大于等于 target 的元素。

class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        // 处理所有元素都小于 target 的情况
        return nums[right] < target ? right + 1 : right;
    }
}

5. 山脉数组的峰顶索引

山脉数组先增后减,峰值即为最大值。利用单调性,比较中间元素与其右侧元素即可判断峰值在哪一侧。

class Solution {
    public int peakIndexInMountainArray(int[] arr) {
        int left = 0;
        int right = arr.length - 1;
        while (left < right) {
            int mid = left + (right - left + 1) / 2;
            if (arr[mid - 1] < arr[mid]) {
                // 处于上升段,峰值在右侧
                left = mid;
            } else {
                // 处于下降段,峰值在左侧
                right = mid - 1;
            }
        }
        return left;
    }
}

6. 寻找峰值

任意一个峰值均可。只要 nums[i] != nums[i+1],数组中必然存在峰值。比较 mid 与 mid+1 可确定方向。

class Solution {
    public int findPeakElement(int[] nums) {
        int left = 0;
        int right = nums.length - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] > nums[mid + 1]) {
                // 下降趋势,峰值在左侧(含 mid)
                right = mid;
            } else {
                // 上升趋势,峰值在右侧
                left = mid + 1;
            }
        }
        return left;
    }
}

7. 寻找旋转排序数组中的最小值

原有序数组经过旋转,分为两段递增序列。以最后一个元素为基准,可以将数组分为 > nums[n-1] 和 <= nums[n-1] 两部分。

class Solution {
    public int findMin(int[] nums) {
        int right = nums.length - 1;
        int n = right;
        int left = 0;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] > nums[n]) {
                // 最小值在右侧
                left = mid + 1;
            } else {
                // 最小值在左侧(含 mid)
                right = mid;
            }
        }
        return nums[left];
    }
}

8. 点名(缺失数字)

给定递增数组,下标与值一一对应,找出缺失的那个数字。除了哈希、求和、位运算外,二分法同样适用。

class Solution {
    public int takeAttendance(int[] records) {
        int left = 0;
        int right = records.length - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (records[mid] == mid) {
                // 前半部分正常,缺失在右侧
                left = mid + 1;
            } else {
                // 后半部分异常,缺失在左侧(含 mid)
                right = mid;
            }
        }
        return records[left] == left ? left + 1 : left;
    }
}

其他解法如哈希表、直接遍历、求和公式等虽然可行,但在数据量较大时效率不如二分查找稳定。掌握二分的变体逻辑,比死记硬背代码更重要。

目录

  1. 二分算法核心思路
  2. 1. 标准二分查找
  3. 2. 在排序数组中查找元素的第一个和最后一个位置
  4. 3. x 的平方根
  5. 4. 搜索插入位置
  6. 5. 山脉数组的峰顶索引
  7. 6. 寻找峰值
  8. 7. 寻找旋转排序数组中的最小值
  9. 8. 点名(缺失数字)

更多推荐文章

查看全部
  • Spring Boot 结合 Leaflet 实现省级旅游口号 WebGIS 可视化
  • Python缠论分析指南:代码实现精准买卖点识别
  • HarmonyOS6 ArkTS Tabs 组件 TabBar 布局模式配置指南
  • Python:从数据科学到全栈开发的万能工具
  • ComfyUI Photoshop 插件安装与使用指南
  • 十大车企借助大模型革新汽车产业实践案例
  • C++ 模板:泛型编程与代码复用
  • Catch2 现代 C++ 测试框架实战指南
  • 美团 EvoCUA 刷新开源 SOTA:具备持续进化能力的计算机操作智能体
  • 基于 Web 的工业设备监测系统架构设计:非功能需求与数据访问机制
  • Ollama 与 FastGPT 本地私有化大模型部署指南
  • GitHub 上值得参考的量化交易开源项目
  • Linux 常用指令与压缩解压操作指南
  • 深入理解 Sentinel:分布式系统流量控制与熔断降级
  • Kiro AI 助手:核心功能与实战指南
  • AXI 总线详解与 FPGA 实现指南
  • Whisper 模型跨平台下载与部署指南
  • 从零开始学习大模型:第一章 大模型基础与入门指南
  • AI 辅助编程工具对比:Copilot、Cursor、Claude Code 与 Codex 解析
  • Python AI 开源工具精选:从 Agent 框架到大模型推理实战

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online