day126—二分查找—寻找旋转排序数组中的最小值(LeetCode-153)

题目描述

已知一个长度为 n 的数组,预先按照升序排列,经由 1n旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:

  • 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
  • 若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]

注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]]

给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

复制代码
输入:nums = [3,4,5,1,2]
输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。

示例 2:

复制代码
输入:nums = [4,5,6,7,0,1,2]
输出:0
解释:原数组为 [0,1,2,4,5,6,7] ,旋转 4 次得到输入数组。

示例 3:

复制代码
输入:nums = [11,13,15,17]
输出:11
解释:原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。

提示:

  • n == nums.length
  • 1 <= n <= 5000
  • -5000 <= nums[i] <= 5000
  • nums 中的所有整数 互不相同
  • nums 原来是一个升序排序的数组,并进行了 1n 次旋转

解决方案:

代码逻辑拆解

  1. 边界初始化

    • len = nums.size():获取数组长度;
    • left = -1right = len - 1:延续「开区间 (left, right)」的二分查找设计,初始查找范围是 (-1, len-1),这种设计能避免处理数组首尾时的越界问题;
    • mid = 0:用于存储二分查找的中间索引。
  2. 二分循环条件

    • while(left + 1 < right):这是开区间二分的经典终止条件,保证 (left, right) 区间内还有至少一个元素可检查;当 left + 1 == right 时,区间内无剩余元素,循环终止。
  3. 核心的区间收缩逻辑(关键) :旋转排序数组的核心特性是:数组被旋转后分成左右两个升序子数组 ,且左侧子数组的所有元素 ≥ 右侧子数组的所有元素,而 nums[len-1] 是右侧子数组的最后一个元素(也是右侧子数组的最大值)。基于此:

    • 计算中间位置 mid = (left + right) / 2
    • nums[mid] < nums[len-1]:说明 mid 落在右侧的升序子数组 (最小值所在的子数组),最小值一定在 mid 左侧(包括 mid),因此将 right = mid 缩小区间;
    • 否则(nums[mid] ≥ nums[len-1]):说明 mid 落在左侧的升序子数组 (数值更大的子数组),最小值一定在 mid 右侧,因此将 left = mid 缩小区间。
  4. 结果返回 :循环结束时 left + 1 == right,此时 right 恰好指向数组最小元素的索引,返回 nums[right] 即可得到最小值。

示例验证

nums = [4,5,6,1,2,3] 为例:

  • 初始:left=-1right=5nums[5]=3);
  • 第一次循环:mid=2nums[2]=6 > 3left=2
  • 第二次循环:mid=3nums[3]=1 < 3right=3
  • 此时 left+1=3 == right=3,循环终止,返回 nums[3]=1(正确)。

总结

  1. 算法利用旋转排序数组的特性 (最后一个元素区分左右升序子数组),通过二分查找将时间复杂度优化至 O(log n)
  2. 「开区间 (left, right)」的边界设计简化了边界处理,无需额外判断数组是否旋转;
  3. 核心判断条件是比较 nums[mid]nums[len-1],以此确定最小值所在区间,最终 right 指向最小值索引。

函数源码:

cpp 复制代码
class Solution {
public:
    int findMin(vector<int>& nums) {
        int len=nums.size();
        int right=len-1;
        int left=-1;
        int mid=0;
        
        while(left+1<right){
            mid=(right+left)/2;
            if(nums[mid]<nums[len-1]){
                right=mid;
            }else{
                left=mid;
            }
        }
        return nums[right];
    }
};
相关推荐
乌萨奇也要立志学C++3 分钟前
【洛谷】BFS 求解最短路:从马的遍历到迷宫问题的实战解析
算法·宽度优先
老鼠只爱大米12 分钟前
LeetCode经典算法面试题 #46:全排列(回溯、交换、剪枝等五种实现方案详细解析)
算法·leetcode·剪枝·回溯·全排列·stj算法
Dovis(誓平步青云)25 分钟前
《滑动窗口算法:从 “暴力遍历” 到 “线性高效” 的思维跃迁》
运维·服务器·数据库·算法
_OP_CHEN1 小时前
【算法基础篇】(五十七)线性代数之矩阵乘法从入门到实战:手撕模板 + 真题详解
线性代数·算法·矩阵·蓝桥杯·c/c++·矩阵乘法·acm/icpc
天天爱吃肉82181 小时前
【跨界封神|周杰伦×王传福(陶晶莹主持):音乐创作与新能源NVH测试,底层逻辑竟完全同源!(新人必看入行指南)】
python·嵌入式硬件·算法·汽车
im_AMBER1 小时前
Leetcode 114 链表中的下一个更大节点 | 删除排序链表中的重复元素 II
算法·leetcode
xhbaitxl1 小时前
算法学习day38-动态规划
学习·算法·动态规划
多恩Stone1 小时前
【3D AICG 系列-6】OmniPart 训练流程梳理
人工智能·pytorch·算法·3d·aigc
历程里程碑1 小时前
普通数组----轮转数组
java·数据结构·c++·算法·spring·leetcode·eclipse
pp起床1 小时前
贪心算法 | part02
算法·leetcode·贪心算法