题目
标题和出处
标题:寻找旋转排序数组中的最小值
难度
5 级
题目描述
要求
已知一个长度为 n \texttt{n} n 的数组预先按照升序排序,经过 1 \texttt{1} 1 到 n \texttt{n} n 次旋转。例如,原数组 nums = [0,1,2,4,5,6,7] \texttt{nums = [0,1,2,4,5,6,7]} nums = [0,1,2,4,5,6,7] 在变化后可能得到:
- 若旋转 4 \texttt{4} 4 次,则可以得到 [4,5,6,7,0,1,2] \texttt{[4,5,6,7,0,1,2]} [4,5,6,7,0,1,2]。
- 若旋转 7 \texttt{7} 7 次,则可以得到 [0,1,2,4,5,6,7] \texttt{[0,1,2,4,5,6,7]} [0,1,2,4,5,6,7]。
注意,数组 [a[0], a[1], a[2], ..., a[n-1]] \texttt{[a[0], a[1], a[2], ..., a[n-1]]} [a[0], a[1], a[2], ..., a[n-1]] 旋转一次的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] \texttt{[a[n-1], a[0], a[1], a[2], ..., a[n-2]]} [a[n-1], a[0], a[1], a[2], ..., a[n-2]]。
给定一个元素值各不相同的旋转后的排序数组 nums \texttt{nums} nums,返回数组中的最小元素。
要求时间复杂度是 O(log n) \texttt{O(log n)} O(log n)。
示例
示例 1:
输入:
nums
=
[3,4,5,1,2]
\texttt{nums = [3,4,5,1,2]}
nums = [3,4,5,1,2]
输出:
1
\texttt{1}
1
解释:原数组为
[1,2,3,4,5]
\texttt{[1,2,3,4,5]}
[1,2,3,4,5],旋转
3
\texttt{3}
3 次得到输入数组。
示例 2:
输入:
nums
=
[4,5,6,7,0,1,2]
\texttt{nums = [4,5,6,7,0,1,2]}
nums = [4,5,6,7,0,1,2]
输出:
0
\texttt{0}
0
解释:原数组为
[0,1,2,4,5,6,7]
\texttt{[0,1,2,4,5,6,7]}
[0,1,2,4,5,6,7],旋转
4
\texttt{4}
4 次得到输入数组。
示例 3:
输入:
nums
=
[11,13,15,17]
\texttt{nums = [11,13,15,17]}
nums = [11,13,15,17]
输出:
11
\texttt{11}
11
解释:原数组为
[11,13,15,17]
\texttt{[11,13,15,17]}
[11,13,15,17],旋转
4
\texttt{4}
4 次得到输入数组。
数据范围
- n = nums.length \texttt{n} = \texttt{nums.length} n=nums.length
- 1 ≤ n ≤ 5000 \texttt{1} \le \texttt{n} \le \texttt{5000} 1≤n≤5000
- -5000 ≤ nums[i] ≤ 5000 \texttt{-5000} \le \texttt{nums[i]} \le \texttt{5000} -5000≤nums[i]≤5000
- nums \texttt{nums} nums 中的所有整数各不相同
- nums \texttt{nums} nums 是一个升序排序的数组进行了 1 \texttt{1} 1 至 n \texttt{n} n 次旋转后的数组
解法
思路和算法
为了寻找旋转排序数组中的最小值,需要得到旋转排序数组中的最小值所在下标。
对于长度为 n n n 的升序数组,如果旋转次数等于 n n n 则与旋转前的数组相同,此时最小值位于下标 0 0 0。如果旋转次数小于 n n n 则旋转 i i i 次之后最小值位于下标 i i i。由于数组中的所有整数各不相同,因此当最小值位于下标 i i i 且 0 < i < n − 1 0 < i < n - 1 0<i<n−1 时,下标 i i i 左侧的任意整数一定大于下标 i i i 右侧的任意整数。可以使用二分查找的方法寻找最小值。
用 low \textit{low} low 和 high \textit{high} high 分别表示二分查找的下标范围的下界和上界,初始时 low \textit{low} low 和 high \textit{high} high 分别为数组的最小下标和最大下标。每次查找时, [ low , high ] [\textit{low}, \textit{high}] [low,high] 可能是一个有序子数组或者两个有序子数组。
如果 [ low , high ] [\textit{low}, \textit{high}] [low,high] 是一个有序子数组,则 nums [ low ] < nums [ high ] \textit{nums}[\textit{low}] < \textit{nums}[\textit{high}] nums[low]<nums[high],下标 low \textit{low} low 处的整数就是最小值。
如果 [ low , high ] [\textit{low}, \textit{high}] [low,high] 是两个有序子数组,则 nums [ low ] > nums [ high ] \textit{nums}[\textit{low}] > \textit{nums}[\textit{high}] nums[low]>nums[high],此时需要在范围 [ low , high ] [\textit{low}, \textit{high}] [low,high] 中寻找最小值所在下标。取 mid \textit{mid} mid 为 low \textit{low} low 和 high \textit{high} high 的平均数向下取整,得到下标 mid \textit{mid} mid 处的数,判断该数位于哪一个有序子数组中,调整查找的下标范围。
-
如果 nums [ mid ] < nums [ low ] \textit{nums}[\textit{mid}] < \textit{nums}[\textit{low}] nums[mid]<nums[low],则下标 mid \textit{mid} mid 位于以下标 high \textit{high} high 结束的有序子数组中,最小值位于下标 mid \textit{mid} mid 或其左边,因此在下标范围 [ low , mid ] [\textit{low}, \textit{mid}] [low,mid] 中继续查找。
-
如果 nums [ mid ] ≥ nums [ low ] \textit{nums}[\textit{mid}] \ge \textit{nums}[\textit{low}] nums[mid]≥nums[low],则下标 mid \textit{mid} mid 位于以下标 low \textit{low} low 开始的有序子数组中,最小值位于下标 mid \textit{mid} mid 右边,因此在下标范围 [ mid + 1 , high ] [\textit{mid} + 1, \textit{high}] [mid+1,high] 中继续查找。
二分查找的条件是 low < high \textit{low} < \textit{high} low<high 且 nums [ low ] > nums [ high ] \textit{nums}[\textit{low}] > \textit{nums}[\textit{high}] nums[low]>nums[high],当该条件不成立时,二分查找结束,此时的 low \textit{low} low 即为最小值所在下标。理由如下。
-
如果 low < high \textit{low} < \textit{high} low<high 不成立,则 low = high \textit{low} = \textit{high} low=high,此时二分查找的范围仅限于一个下标。由于被排除的下标都不可能是最小值所在下标,因此 low \textit{low} low 为最小值所在下标。
-
如果 nums [ low ] > nums [ high ] \textit{nums}[\textit{low}] > \textit{nums}[\textit{high}] nums[low]>nums[high] 不成立,则 nums [ low ] < nums [ high ] \textit{nums}[\textit{low}] < \textit{nums}[\textit{high}] nums[low]<nums[high],范围 [ low , high ] [\textit{low}, \textit{high}] [low,high] 中的最小元素位于下标 low \textit{low} low。由于被排除的下标都不可能是最小值所在下标,因此 low \textit{low} low 为最小值所在下标。
代码
class Solution {
public int findMin(int[] nums) {
int low = 0, high = nums.length - 1;
while (low < high && nums[low] > nums[high]) {
int mid = low + (high - low) / 2;
if (nums[mid] < nums[low]) {
high = mid;
} else {
low = mid + 1;
}
}
return nums[low];
}
}
复杂度分析
-
时间复杂度: O ( log n ) O(\log n) O(logn),其中 n n n 是数组 nums \textit{nums} nums 的长度。二分查找的次数是 O ( log n ) O(\log n) O(logn),每次查找的时间是 O ( 1 ) O(1) O(1),时间复杂度是 O ( log n ) O(\log n) O(logn)。
-
空间复杂度: O ( 1 ) O(1) O(1)。
1693

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



