当前位置: 首页 > news >正文

从零开始刷算法——二分-搜索旋转排序数组

一、题目概述

给你一个旋转排序数组,其中没有重复元素,让你在其中查找指定元素 target

例如:

nums = [4,5,6,7,0,1,2]  
target = 0 → 输出 4  
target = 3 → 输出 -1

旋转数组的结构是这样的:

原有序数组:0 1 2 4 5 6 7
旋转 k 次:   4 5 6 7 0 1 2↑       ↑左段仍有序   右段有序

二、我们采用的策略:两次二分

本题最佳方案之一:

✔ 第一次二分:找到旋转点(最小值的位置)

也就是找到整个数组中的 最小值的下标

例如:

[4,5,6,7,0,1,2]  
最小值在 4 号位

第二次二分:选择正确的区间做普通的二分查找

因为旋转后的数组其实是两段独立的有序数组

[4 5 6 7] | [0 1 2]

找到最小值的下标 i,就能得到两个有序区间:

  • 第一区:[0 ... i-1]

  • 第二区:[i ... n-1]

然后根据 target 和 nums.back() 的关系,判断 target 落在哪个有序区间,再进行一次普通二分法。


三、第一次二分:寻找最小值(旋转点)

代码如下:

int findMin(vector<int>& nums) {int left = 0;int right = nums.size() - 1;while (left <= right) {int mid = left + (right - left) / 2;if (nums[mid] > nums.back()) {left = mid + 1;}else {right = mid - 1;}}return left;
}

为什么比较 nums[mid] > nums.back()

因为旋转数组有一个性质:

  • 比末尾大的数字一定在左侧有序段(红区)

  • 比末尾小的数字一定在右侧有序段(蓝区,也就是最小值所在段)

我们用“红蓝染色法”理解:

区域特点与 nums.back() 的关系
红区(左段)值更大nums[mid] > nums.back()
蓝区(右段)值更小(包括最小值)nums[mid] <= nums.back()

目标:

找到 蓝色区域(右段)中的第一个数,它就是最小值。

所以 right = mid - 1 是为了锁定蓝区的第一个元素


四、第二次二分:普通二分查找(带红蓝染色法)

为了在有序区间查找 target,我们写了一个精确的 lower_bound:

int lower_bound(vector<int>& nums, int left, int right, int target) {while (left <= right) {int mid = left + (right - left) / 2;if(nums[mid] < target) { // 左边红区left = mid + 1;}else {                  // 右边蓝区right = mid - 1;}}return nums[left] == target? left : -1; 
}

红区:小于 target
蓝区:大于等于 target
最终 left 会来到 蓝区的第一个位置

如果该位置恰好等于 target,则返回下标,否则返回 -1。


五、主函数逻辑:根据 target 落在哪一段

int search(vector<int>& nums, int target) {int i = findMin(nums);// 第一段if (target > nums.back()){return lower_bound(nums, 0, i - 1, target);}else return lower_bound(nums, i, nums.size() - 1, target);
}

逻辑非常清晰:

  • 若 target > nums.back()
    → target 必在左侧红区 [0 ... i-1]

  • 否则
    → target 在右侧蓝区 [i ... n-1]

这是由旋转数组的结构决定的。

六、完整代码(推荐写法)

class Solution {int findMin(vector<int>& nums) {int left = 0;int right = nums.size() - 1;while (left <= right) {int mid = left + (right - left) / 2;if (nums[mid] > nums.back()) {left = mid + 1;}else {right = mid - 1;}}return left;}int lower_bound(vector<int>& nums, int left, int right, int target) {while (left <= right) {int mid = left + (right - left) / 2;if(nums[mid] < target) { left = mid + 1;}else {right = mid - 1;}}return nums[left] == target? left : -1; }
public:int search(vector<int>& nums, int target) {int i = findMin(nums);if (target > nums.back()){return lower_bound(nums, 0, i - 1, target);}else return lower_bound(nums, i, nums.size() - 1, target);}
};

七、这种两次二分法的优势

① 逻辑清晰,结构稳定

不会出现常见的:

  • 左右边界写反

  • mid 的判断错乱

  • 死循环 / 越界等问题

② 利用红蓝染色法,可严格证明每一步正确

每个区间都有清晰的数学意义。

③ 时间复杂度依旧是 O(log n)

两次二分仍然是 O(log n)。

④ 适合拓展到更多题目

例如 154, 153, 81 等旋转数组题几乎可以模板化处理。


总结

本题的精髓不是“二分”,而是结构化思维——把问题拆成两个独立的二分

  1. 找旋转点(找蓝区第一个数)

  2. 在对应有序段做普通二分(找蓝区第一个等于 target 的数)

用红蓝染色法可以让你彻底理解“为什么 mid > nums.back() 就是红区”。

http://www.dtcms.com/a/609593.html

相关文章:

  • Spring Boot + Vue 全栈开发实战指南
  • 深度智能体-人机回环
  • ASC学习笔记0013:添加一个新的属性集(初始化为默认值)
  • SpringIOC-注解
  • 关闭网站跳转代码wordpress 图片云存储
  • Java练习——数组练习
  • 导航仪企业网站源码那个网站做拍手比较好
  • 离别的十字路口: 是否还记得曾经追求的梦想
  • 3D地球可视化教程 - 第4篇:第5篇:星空背景与粒子系统
  • Unity Shader Graph 3D 实例 - 一个简单的红外线扫描全身效果
  • 【Mastergo】Mastergo总览
  • steam安装游戏为什么磁盘写入错误?磁盘写入错误怎么办?,同样问题解决方案
  • Unity官方文档 Asset Workflow:Importing Assets 阅读笔记
  • UD动作游戏开发读书笔记--. D游戏所需要的数学知识
  • 网站多语切换怎么做网站跳出率多少合适
  • MySQL数据类型全面解析:从数值精度到字符串优化的最佳实践
  • 太阳光模拟器应用:汽车太阳能天窗的发电效能动态测试
  • ES踩了一坑 script查询与float类型的精度
  • ASC学习笔记0015:此能力系统组件是否具有此属性?
  • 如何基于DSL脚本进行elasticsearch向量检索示例
  • 如何利用大语言模型(LLM)实现自动标注与内容增强
  • h5网站有哪些网页设计公司的目标客户有哪些
  • 做网站推广合同做标志的网站
  • 【ZeroRange WebRTC】Amazon Kinesis Video Streams WebRTC Data Plane REST API 深度解析
  • 2025从部署到迭代:Deepseek知识库部署服务商如何护航企业知识管理?
  • 检索增强微调(RAFT)如何重塑慢病健康管理?——从技术原理到落地实践
  • 网站开发 工作量评估关于建设网站安全性合同
  • 学Java第四十四天——可变参数、Collections工具类
  • XQuery FLWOR + HTML:深入理解与实际应用
  • 风丘助力优化ADAS测试:多路雷达、视频及车辆总线数据的集成处