旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个升序的数组的一个旋转,输出旋转数组的最小元素。
例如数组 {3,4,5,1,2}为 {1,2,3,4,5} 的一个旋转,该数组的最小值为 1。
数组可能包含重复项。
注意:数组内所含元素非负,若数组大小为 0,请返回 −1。
数据范围
数组长度 [0,90]。
样例
输入:nums = [2, 2, 2, 0, 1]输出:0
题解
- 预处理:
首先处理数组末尾与首元素相同的元素,以消除末尾水平段对二分查找的影响。例如,数组[3,3,1,3]
处理后会变为[3,3,1]
。 - 完全单调判断:
若处理后的数组满足nums[end] >= nums[0]
,说明数组未被旋转或已完全单调,直接返回首元素。 - 二分查找:
二分查找的目标是找到旋转点(即最小值的位置)。根据以下条件调整边界:- 若
nums[mid] < nums[0]
,说明mid
位于右半段(旋转后的较小部分),将右边界设为mid
。 - 否则,将左边界设为
mid + 1
。
- 若
复杂度分析
- 时间复杂度:
- 预处理阶段最坏需要遍历整个数组(例如所有元素相同),时间复杂度为 O ( n ) O(n) O(n)。
- 二分查找的时间复杂度为 O ( log n ) O(\log n) O(logn)。
- 总时间复杂度为 O ( n ) O(n) O(n)。
- 空间复杂度:
仅使用常数空间,复杂度为 O ( 1 ) O(1) O(1)。
class Solution {
public:int findMin(vector<int>& nums) {int n = nums.size() - 1;if (n < 0) return -1; // 处理空数组// 删除末尾与首元素相同的元素while (n > 0 && nums[n] == nums[0]) n--;// 若数组完全单调,直接返回首元素if (nums[n] >= nums[0]) return nums[0];// 二分查找最小值int l = 0, r = n;while (l < r) {int mid = (l + r) >> 1; // 等价于 (l + r) / 2if (nums[mid] < nums[0]) r = mid;else l = mid + 1;}return nums[r];}
};
代码解释
- 预处理:
通过while
循环删除末尾与首元素相同的元素,例如[3,3,1,3]
变为[3,3,1]
。 - 完全单调判断:
若处理后的数组满足nums[end] >= nums[0]
(例如[1,2,3,4]
),说明未被旋转,直接返回首元素。 - 二分查找:
- 循环条件:
l < r
确保区间最终收敛到单个元素。 - 中间值计算:
mid = (l + r) >> 1
取中间位置。 - 边界调整:若
nums[mid] < nums[0]
,说明最小值在左半段(包含mid
),否则在右半段(mid + 1
开始)。 - 返回值:最终
r
指向最小值的位置。
- 循环条件: