《剑指 Offer》学习记录:题 11:旋转数组的最小数字
题 11:旋转数组的最小数字
题干
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。——《剑指 Offer》P82
测试样例
样例一
普通测试点。
输入:
[3,4,5,1,2]
输出:
1
样例二
带有重复数字的数组的旋转。
输入:
[2,2,2,0,1]
输出:
0
样例三
最小值是数组的最后一个元素。
[2,2,2,0]
输出:
0
样例四
最小值是数组的第一个或最后一个元素。
[1,2,1]
输出:
1
样例五
数组旋转的元素个数是 0 个,也就是传了原始的数组进来。
[1,2,3]
输出:
1
样例六
数组中只出现过一个数字。
[1,1]
输出:
1
遍历查找
解题思路
这个做法没什么好说的,就是直接遍历一遍找到数组的最小值。虽然不能减少 O(n),但是可以减少 T(n)。观察上述的样例,显然当 nums[i] < nums[i-1] 时 nums[i] 就是数组的最小值。如果用这种方式找不到最小值,说明传入的数组的旋转元素个数为 0(对应样例五、六),这时应该返回第一个元素。
题解代码
class Solution:
def minArray(self, numbers: List[int]) -> int:
for i in range(1, len(numbers)):
if numbers[i] < numbers[i - 1]:
return numbers[i]
return numbers[0]
时空复杂度
最坏情况下需要把数组遍历一遍,所以时间复杂度为 O(n)。
由于只需要常数个数的辅助变量,因此空间复杂度为 O(1)。
二分查找
解题思路
这种方法是比较聪明的,观察下图所示的样例,发现所谓数组的旋转是把原本的数组分割成了 2 个递增的序列,且第一个序列的元素均不小于第二个序列的任何元素,而最小值是 2 个序列的界限。
此时的目的就变成了找到第 2 个递增序列的第一个元素,可以使用二分法来查找。定义 2 分查找的 3 个变量 high、mid、low,根据第一个序列的元素均不小于第二个序列的任何元素的特点,当 nums[mid] > nums[low] 时,说明待查找的元素在 mid 的右侧,需要执行 “high = mid + 1”;当 nums[mid] < nums[low] 时说明待查找的元素在 mid 的左侧,需要执行 “low = mid”。最终当 high >= low 时查找结束,直接返回 nums[high] 就行。
例如上面的样例,初始情况下 3 个变量 high、mid、low 的状态如下。此时由于 nums[mid] > nums[low] 说明待查找的元素在 mid 的右侧,需要执行 “high = mid + 1”。
第一轮二分以后 3 个变量 high、mid、low 的状态如下,此时由于 nums[mid] < nums[low] 说明待查找的元素在 mid 的左侧,需要执行 “low = mid”。
第二轮二分以后 3 个变量 high、mid、low 的状态如下,此时由于 nums[mid] < nums[low] 说明待查找的元素在 mid 的左侧,需要执行 “low = mid”。
第三轮二分以后 3 个变量 high、mid、low 的状态如下,此时由于不满足 high < low 的条件,二分查找结束返回 nums[high] 解决问题。
注意使用二分时可以能会遇到 nums[mid] = nums[low] 的情况,此时无法判断说明待查找的元素在数组的什么地方。遇到这种情况有 2 种解决方法,一种是直接使用遍历查找,另一种是用减治法令 low = low - 1 减小解空间的范围,其实换成遍历查找也是减治法。
题解代码
class Solution:
def minArray(self, numbers: List[int]) -> int:
high = 0
low = len(numbers) - 1
while high < low:
mid = int((high + low) / 2)
if numbers[mid] > numbers[low]:
high = mid + 1
elif numbers[mid] < numbers[low]:
low = mid
else:
low = low - 1
return numbers[high]
时空复杂度
由于使用二分法一次可以将查找范围缩小一半,所以时间复杂度为 O(㏒n)。
由于只需要常数个数的辅助变量,因此空间复杂度为 O(1)。
参考资料
《剑指 Offer(第2版)》,何海涛 著,电子工业出版社
面试题11. 旋转数组的最小数字(二分法,清晰图解)