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

二分查找算法进阶:山脉数组与旋转排序

综述由AI生成通过四个经典力扣题目(山脉数组峰顶、寻找峰值、旋转排序数组最小值、点名缺失数字),深入讲解二分查找算法的不同应用场景。分别对比了二分查找与暴力枚举、哈希表、位运算及数学方法的优劣,重点分析了如何利用单调性或区间特性优化查找过程,时间复杂度从 O(n) 降至 O(logN)。

PhpPioneer发布于 2026/3/22更新于 2026/5/2322 浏览
二分查找算法进阶:山脉数组与旋转排序

一、852.山脉数组的峰顶索引

题目链接:852.山脉数组的峰顶索引

题目解析:

  • 给我们一个数组,元素是先递增在递减的,让我们返回最大元素下标。

1.1 二分查找

解题思路:

  • 我们这个数组本来就是一个被天然分成两段,递增区间和递减区间,的数据,天然使用二分查找的题。
  • 当 mid 的元素小于后一个元素的时候,mid 在递增区间,所以 left = mid + 1。
  • 当 mid 的元素大于后一个元素的时候,mid 在递减区间,所以 right = mid。

解题代码:

//时间复杂度:O(logN)
//空间复杂度:O(1)
class Solution {
    public int peakIndexInMountainArray(int[] arr) {
        int left = 0;
        int right = arr.length - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (arr[mid] < arr[mid + 1]) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        return left;
    }
}

1.2 暴力枚举

解题思路:

  • 直接使用 for 循环遍历数组,该下标元素大于前面和后面元素,返回下标。
  • 因为这个数组一定是一个山脉数组,所以不用管数组头和尾。

解题代码:

//时间复杂度:O(n)
//空间复杂度:O(1)
class Solution {
    public int peakIndexInMountainArray(int[] arr) {
        for (int i = 1; i < arr.length - 1; i++) {
            if (arr[i] > arr[i - 1] && arr[i] > arr[i + 1]) {
                return i;
            }
        }
        return 0;
    }
}

二、162.寻找峰值

题目链接:162.寻找峰值

题目解析:

  • 给我们的数组是,递增递减反复进行,让我们找到其中一个峰值的下标。
  • 数组可能是这几个情况:1. ///\ ,2. / ,3. \

2.1 二分查找

解题思路:

  • 其实我们可以将数组分为,有要求峰值和没有要求峰值两段,因为给了峰值不等于下一个元素。
  • 所以当 mid 小于下一个元素的时候,mid 就是在没有要求峰值那段,left = mid+1。
  • 当 mid 大于下一个元素的时候,mid 就是在有峰值那段,right = mid

解题代码:

//时间复杂度:O(logN)
//空间复杂度:O(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]) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        return left;
    }
}

2.2 暴力枚举

解题思路:

  • 直接转变为求数组最大值下标,遍历数组即可。

解题代码:

//时间复杂度:O(n)
//空间复杂度:O(1)
class Solution {
    public int findPeakElement(int[] nums) {
        int ret = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] > nums[ret]) {
                ret = i;
            }
        }
        return ret;
    }
}

三、153.寻找旋转排序数组中的最小值

题目链接:153.寻找旋转排序数组中的最小值

题目解析:

  • 数组旋转一次就代表将数组尾元素放在数组头。
  • 给我们一个原来升序的数组,旋转多次后,找到数组中最小元素下标。
  • 数组中元素各不相同。

数组会是下面这种状态或者一个完全递增的状态

3.1 二分查找

解题思路:

  • 我们这个数组本来就是已经是被分成两段了的。
  • 我们可以使用 nums[0] 或者 nums[nums.length - 1] 来当分界线。
  • 使用 nums[nums.length - 1] 为界限:
    • mid 元素大于等于界限时,在数组段 1,所以 left = mid + 1。
    • mid 元素小于界限的时候,在数组段 2,所以 right = mid。
  • 使用 nums[0] 为界限:
    • mid 元素大于等于界限时,在数组段 1,所以 left = mid + 1。
    • mid 元素小于界限的时候,在数组段 2,所以 right = mid。
  • 考虑数组完全递增时,nums[0] 才是最小值。

解题代码:

//时间复杂度:O(logN)
//空间复杂度:O(1)
//使用 nums[ nums.length - 1 ] 为界限:
class Solution {
    public int findMin(int[] nums) {
        int left = 0;
        int right = nums.length - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < nums[nums.length - 1]) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return nums[left];
    }
}

//使用 nums[ 0 ] 为界限:
class Solution {
    public int findMin(int[] nums) {
        int left = 0;
        int right = nums.length - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] >= nums[0]) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        if (nums[0] < nums[left]) {
            left = 0;
        }
        return nums[left];
    }
}

3.2 暴力枚举

解题思路:

  • 直接循环遍历数组找最小值即可。

解题代码:

//时间复杂度:O(n)
//空间复杂度:O(1)
class Solution {
    public int findMin(int[] nums) {
        int ret = nums[0];
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] < ret) {
                ret = nums[i];
            }
        }
        return ret;
    }
}

四、LCR 173.点名

题目链接:LCR 173.点名

题目解析:

  • 给你一个长度为 n 数组,这个数组中有 0 到 n 中的数,按升序排列,找出数组在 0 到 n 中不含有的数。

4.1 二分查找

解题思路:

  • 这个数组分为这样两段,一段是下标与元素值相同的,一段是下标是元素值减 1。
  • 所以当 mid 元素等于 mid 的时候,落在第一段,left = mid + 1;
  • 当 mid 元素不等于 mid 的时候,落在第二段,right = mid。

解题代码:

//时间复杂度:O(logN)
//空间复杂度:O(1)
class Solution {
    public int takeAttendance(int[] records) {
        int left = 0;
        int right = records.length;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (records[mid] == mid) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        return left;
    }
}

4.2 哈希表

解题思路:

  • 借助一个数组来标记 0 到 n 中出现过的元素。

解题代码:

//时间复杂度:O(n)
//空间复杂度:O(n)
class Solution {
    public int takeAttendance(int[] records) {
        int[] hash = new int[records.length + 1];
        for (int i = 0; i < records.length; i++) {
            hash[records[i]]++;
        }
        for (int i = 0; i < hash.length; i++) {
            if (hash[i] == 0) {
                return i;
            }
        }
        return 0;
    }
}

4.3 暴力枚举

解题思路:

  • 直接遍历数组,当出现第一个下标和元素不相等的直接返回元素减一即可。
  • 遍历完数组还是没找到,证明是 0 到 n 中 n 不在数组中。

解题代码:

//时间复杂度:O(n)
//空间复杂度:O(1)
class Solution {
    public int takeAttendance(int[] records) {
        for (int i = 0; i < records.length; i++) {
            if (records[i] != i) {
                return records[i] - 1;
            }
        }
        return records.length;
    }
}

4.4 位运算

解题思路:

  • 直接使用一个变量来将 0 到 n 的数全部异或。
  • 在于数组元素进行异或。

解题代码:

//时间复杂度:O(n)
//空间复杂度:O(1)
class Solution {
    public int takeAttendance(int[] records) {
        int res = 0;
        int n = records.length;
        // 异或 0 到 n
        for (int i = 0; i <= n; i++) {
            res ^= i;
        }
        // 异或数组元素
        for (int x : records) {
            res ^= x;
        }
        return res;
    }
}

4.5 数学(求和)

解题思路:

  • 直接先将 0 到 n 的和求出来。
  • 在减去数组中的元素即可。

解题代码:

//时间复杂度:O(n)
//空间复杂度:O(1)
class Solution {
    public int takeAttendance(int[] records) {
        int n = records.length;
        int ret = (n * (n + 1)) / 2;
        for (int i = 0; i < records.length; i++) {
            ret = ret - records[i];
        }
        return ret;
    }
}

目录

  1. 一、852.山脉数组的峰顶索引
  2. 1.1 二分查找
  3. 1.2 暴力枚举
  4. 二、162.寻找峰值
  5. 2.1 二分查找
  6. 2.2 暴力枚举
  7. 三、153.寻找旋转排序数组中的最小值
  8. 3.1 二分查找
  9. 3.2 暴力枚举
  10. 四、LCR 173.点名
  11. 4.1 二分查找
  12. 4.2 哈希表
  13. 4.3 暴力枚举
  14. 4.4 位运算
  15. 4.5 数学(求和)
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • VS2022 无法使用 Copilot 的网络问题排查与解决
  • Python 调用智谱 GLM-4V 实现图片视觉识别与验证码解析
  • GitHub 日榜精选:2025 年 11 月 16 日热门开源项目
  • 高德地图 JSAPI 加载器集成与 Key 配置指南
  • DeepSeek 深度使用指南:提示词技巧与本地知识库搭建
  • Python 多进程开销解析与 IPC 优化实战
  • Qwen3-VL-WEBUI 开箱即用:Instruct 与 Thinking 模式实战
  • SQL 注入绕过函数过滤:GBK 宽字节攻击
  • Triton-Copilot 人机协同设计哲学:超越传统代码生成器
  • 大语言模型架构解析:稀疏门控混合专家(MoE)模型
  • 使用 Python 通过 WebSocket 获取股票 Tick 级实时行情
  • 无人机基本组成与结构设计
  • Linux 文件描述符与重定向实战:从原理到 minishell 实现
  • Open WebUI 本地部署与远程 Ollama 集成实战
  • Spring Boot 项目运行 JUnit 测试报 NoSuchMethodError 错误处理
  • LoRA 训练入门:如何定制专属 AI 绘画模型
  • JVM 从底层原理到实战调优全维度深度解析
  • libwebkit2gtk-4.1-0 安装全流程与配置说明
  • FLUX.1-dev 与 SDXL 像素艺术生成质量深度对比
  • Python format() 方法详解:灵活通用的字符串格式化方案

相关免费在线工具

  • 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