二分查找经典——力扣153.寻找旋转排序数组中的最小值
力扣153.寻找旋转排序数组中的最小值
【二分查找经典】寻找旋转排序数组中的最小值 —— 力扣 153题
前言
在面试中,二分查找几乎是“必考题型”。很多人以为二分查找只是用来“找某个数”,但真正的考察往往是:能否灵活运用二分的思想,解决那些稍微变形的场景。
这道题——寻找旋转排序数组中的最小值,就是二分查找的经典应用之一。它看起来只是一个数组最小值问题,但要求时间复杂度必须是 O(log n),这就直接把你推向了二分查找的怀抱。
如果你还停留在套模板的阶段,这道题一定会给你新的启发:二分查找的核心不是“找数”,而是“缩小边界,逐步逼近答案”。
一、题目描述
已知一个长度为 n
的数组,预先按照升序排列,经由 1
到 n
次 旋转 后,得到输入数组。
-
例如,原数组
nums = [0,1,2,4,5,6,7]
在变化后可能得到:- 若旋转 4 次,则可以得到
[4,5,6,7,0,1,2]
- 若旋转 7 次,则可以得到
[0,1,2,4,5,6,7]
- 若旋转 4 次,则可以得到
注意,数组 [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
示例 2
输入:nums = [4,5,6,7,0,1,2]
输出:0
示例 3
输入:nums = [11,13,15,17]
输出:11
提示
n == nums.length
1 <= n <= 5000
-5000 <= nums[i] <= 5000
nums
中的所有整数互不相同nums
原来是一个升序数组,并进行了1
至n
次旋转
二、题目分析
这道题的关键点:
- 原始数组是升序排列的。
- 旋转后,数组被拆成了两段有序的部分。
- 我们要找到那段断裂处的最小值。
- 要求 O(log n),直指 二分查找。
直观想法:
- 如果数组没有被旋转,比如
[1,2,3,4,5]
,最小值就是nums[0]
。 - 如果数组被旋转,比如
[4,5,6,7,0,1,2]
,最小值就是右半部分的第一个元素。
那么问题就转化为:
如何用二分定位这个最小值所在的区间?
三、解法探讨
方法一:暴力扫描(O(n))
最简单的想法是直接遍历数组,找到最小值。
代码很简单,但时间复杂度是 O(n),不符合题目要求。
方法二:二分查找(O(log n))【推荐解法】
核心思路:
-
初始化左右指针
left = 0, right = nums.length - 1
。 -
每次取中点
mid
:- 如果
nums[mid] > nums[right]
,说明最小值在 右半部分; - 否则,最小值在 左半部分或就是 mid。
- 如果
-
循环结束时,
left == right
,即最小值位置。
代码实现
class Solution {public int findMin(int[] nums) {int left = 0, right = nums.length - 1;while (left < right) {int mid = left + (right - left) / 2;if (nums[mid] > nums[right]) {// 最小值在右半部分left = mid + 1;} else {// 最小值在左半部分(包括 mid)right = mid;}}return nums[left];}
}
方法三:小优化的二分查找
其实我们还能加个小优化:
- 如果
nums[left] < nums[right]
,说明数组根本没被旋转,直接返回nums[left]
。
代码实现
class Solution {public int findMin(int[] nums) {if (nums[0] < nums[nums.length - 1]) {return nums[0]; // 没有旋转}int left = 0, right = nums.length - 1;while (left < right) {int mid = left + (right - left) / 2;if (nums[mid] > nums[right]) {left = mid + 1;} else {right = mid;}}return nums[left];}
}
这个小判断能减少一部分计算量,虽然对复杂度没影响,但写起来更优雅。
四、复杂度分析
-
时间复杂度:
- 方法一:O(n)
- 方法二/三(二分):O(log n) ✅ 符合要求
-
空间复杂度:
- O(1),仅使用了常数级变量
五、总结
这道题本质上是 二分查找的边界问题。
- 核心是通过比较
nums[mid]
与nums[right]
,判断最小值落在哪一半。 - 题目要求 O(log n),暴力方法直接淘汰,二分才是正解。
- 如果你能把这种思路融会贯通,会发现很多旋转数组相关题目其实都是换汤不换药。
面试视角小结:
-
这题常出现在一线大厂的面试中,考察点就是“能不能在变形场景里用对二分查找”。
-
常见误区:
- 直接遍历,没注意到复杂度要求。
- 二分写错边界,死循环。
-
掌握这题,相当于对二分查找的理解更深了一层。
一句话总结:
旋转数组找最小值,记住用二分锁定断点,一步一步缩小区间,最终就能找到答案。