LeetCode 153. 寻找旋转排序数组中的最小值
问题描述
给定一个长度为 n 的升序排列数组,经过 1 到 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 次得到输入数组。
算法思路
核心:二分查找的变种
- 旋转数组特性:
- 数组由两个有序部分组成:左段(较大值)和右段(较小值)
- 最小值是右段的
第一个元素
- 二分查找策略:
- 比较中间元素
nums[mid]与右边界nums[right] - 若
nums[mid] > nums[right]:最小值在右半段(left = mid + 1) - 若
nums[mid] < nums[right]:最小值在左半段(right = mid)
- 比较中间元素
代码实现
class Solution {
public int findMin(int[] nums) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = left + (right - left) / 2; // 避免溢出,mid = left
// 中间值大于右边界:最小值在右半段
if (nums[mid] > nums[right]) {
left = mid + 1;
}
// 中间值小于等于右边界:最小值在左半段(含mid)
else {
right = mid;
}
}
return nums[left]; // left == right 时找到最小值
}
}
算法分析
- 时间复杂度:O(log n)
每次循环将搜索范围减半。 - 空间复杂度:O(1)
仅使用常数空间。
算法过程
输入 nums = [4,5,6,7,0,1,2] :
初始:left=0, right=6
1: mid=3 → nums[3]=7 > nums[6]=2 → left=4
2: [0,1,2] 中 mid=5 → nums[5]=1 < nums[6]=2 → right=5
3: [0,1] 中 mid=4 → nums[4]=0 < nums[5]=1 → right=4
结束:left=right=4 → 最小值 nums[4]=0
测试用例
public static void main(String[] args) {
Solution solution = new Solution();
// 测试用例1:标准旋转
int[] nums1 = {4,5,6,7,0,1,2};
System.out.println("Test 1: " + solution.findMin(nums1)); // 0
// 测试用例2:旋转到最小值在开头
int[] nums2 = {0,1,2,4,5,6,7};
System.out.println("Test 2: " + solution.findMin(nums2)); // 0
// 测试用例3:最小值在中间
int[] nums3 = {7,0,1,2,4,5,6};
System.out.println("Test 3: " + solution.findMin(nums3)); // 0
// 测试用例4:完全逆序
int[] nums4 = {3,2,1};
System.out.println("Test 4: " + solution.findMin(nums4)); // 1
// 测试用例5:两个元素
int[] nums5 = {2,1};
System.out.println("Test 5: " + solution.findMin(nums5)); // 1
// 测试用例6:单元素
int[] nums6 = {5};
System.out.println("Test 6: " + solution.findMin(nums6)); // 5
// 测试用例7:严格递增(旋转后)
int[] nums7 = {1,2,3,4,5};
System.out.println("Test 7: " + solution.findMin(nums7)); // 1
}
关键点
-
比较对象选择:
- 使用
nums[mid]与nums[right]比较(而非nums[left]) - 原因:当数组完全有序时,
nums[left]可能误导判断(如测试用例7)
- 使用
-
边界更新逻辑:
nums[mid] > nums[right]→left = mid + 1
(最小值在右半段,且mid不可能是最小值)nums[mid] < nums[right]→right = mid
(最小值在左半段,且mid可能是最小值)
-
循环终止条件:
left < right保证最终left == right时退出- 此时指向最小值
常见问题
1. 为什么不用比较 nums[left]?
当数组完全有序时(如 [1,2,3,4,5]):
- 若比较
nums[mid]和nums[left]:3>1会错误移动左边界 - 正确做法:比较
nums[mid]和nums[right]→3<5移动右边界
2. 如何处理重复元素?
本题假设数组元素唯一。若存在重复元素(如 LeetCode 154),需额外处理:
- 当
nums[mid] == nums[right]时,right--
(因为无法判断最小值位置)
3. 为什么 right = mid 而不是 mid - 1?
当 nums[mid] < nums[right] 时,mid 可能是最小值(如测试用例5的 [2,1]),不能跳过。
4. 最坏情况时间复杂度?
二分查找保证最坏情况下仍为 O(log n),例如:
- 每次排除一半元素
- 即使最小值在中间(如测试用例3),复杂度不变
5. 算法是否兼容旋转 0 次的情况?
兼容。当数组完全有序时:
- 每次比较
nums[mid] < nums[right]→right = mid - 最终收敛到
left=0(最小值位置)
833

被折叠的 条评论
为什么被折叠?



