📝 题目描述

题目链接寻找旋转排序数组中的最小值

已知一个长度为 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(logn)O(log n) 的算法解决此问题。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
示例 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 原来是一个升序排序的数组,并进行了 1 至 n 次旋转

💡 解题思路

方法一:二分查找

如下图所示,中间值 mid 和最小值的关系共三种情况。

第一种情况:即 nums[left] <= nums[mid] <= nums[right],这时只需返回 nums[left] 即可。

第二种情况:即 nums[left] <= nums[mid],但是 nums[mid] >= nums[right],说明 区间 [left, mid] 为升序,最小值只能在 mid 的右边,故更改左边界 left = mid + 1

第三种情况:即 nums[mid] <= nums[right],且 nums[left] >= nums[mid],说明 区间 [mid, right] 为升序,最小值可能是 mid 或者 mid 的左边,故更改右边界 right = mid

🔧 代码实现

1、二分查找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public:
int findMin(vector<int>& nums) {
int left = 0, right = nums.size() - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
// 区间 [left,right] 收缩到了一个递增的区间
if (nums[left] <= nums[mid] && nums[mid] <= nums[right]) {
return nums[left];
//最小值在索引 mid 的右侧
} else if (nums[left] <= nums[mid]) {
left = mid + 1;
//最小值在索引 mid (含)的左侧
} else if (nums[mid] <= nums[right]){
right = mid;
}
}
return -1;
}
};

📊 复杂度分析

1、二分查找

  • 时间复杂度O(logn)O(logn),在每一次 while 循环中,都会将搜索的区间长度减半。
  • 空间复杂度O(1)O(1),只使用了几个常数级别的整型变量。

🎯 总结

  • 核心思想:掌握旋转数组类的模板写法。