1 题目描述
来源:力扣(LeetCode)
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
2 解题思路
如下图所示,寻找旋转数组的最小元素即为寻找右排序数组的首个元素 numbers[x] ,称 x 为 旋转点 。
排序数组的查找问题首先考虑使用二分法解决,其可将遍历法的线性级别时间复杂度降低至对数级别。
(1)循环二分: 设置 i, j 指针分别指向 numbers 数组左右两端,m = (i + j) // 2 为每次二分的中点( “//” 代表向下取整除法,因此恒有 i≤m≤j ),可分为以下三种情况:
- 当 numbers[m] > numbers[j]时: m 一定在 左排序数组 中,即旋转点 x 一定在 [m + 1, j] 闭区间内,因此执行 i = m + 1;
- 当 numbers[m] < numbers[j] 时: m 一定在 右排序数组 中,即旋转点 x 一定在[i, m] 闭区间内,因此执行 j = m;
- 当 numbers[m] == numbers[j] 时: 无法判断 m 在哪个排序数组中,即无法判断旋转点 x 在 [i, m] 还是 [m + 1, j] 区间中。解决方案: 执行 j = j - 1 缩小判断范围 (分析见以下内容) 。
(2)返回值: 当 i = j 时跳出二分循环,并返回 numbers[i] 即可。
3 代码实现(Python3)
class Solution:
def minArray(self, numbers: [int]) -> int:
i, j = 0, len(numbers) - 1
while i < j:
m = (i + j) // 2
if numbers[m] > numbers[j]: i = m + 1
elif numbers[m] < numbers[j]: j = m
else: j -= 1
return numbers[i]
4 复杂度分析
- 时间复杂度 O(log_2 N): 在特例情况下(例如 [1, 1, 1, 1]),会退化到 O(N)。
- 空间复杂度 O(1) : i , j , m 指针使用常数大小的额外空间。。
深入了解复杂度:数据分析学习总结笔记11:空间复杂度和时间复杂度