《剑指 Offer》学习记录:题 11:旋转数组的最小数字

题 11:旋转数组的最小数字#

题干#

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。——《剑指 Offer》P82

测试样例#

样例一#

普通测试点。
输入:

Copy Highlighter-hljs
[3,4,5,1,2]

输出:

Copy Highlighter-hljs
1

样例二#

带有重复数字的数组的旋转。
输入:

Copy Highlighter-hljs
[2,2,2,0,1]

输出:

Copy Highlighter-hljs
0

样例三#

最小值是数组的最后一个元素。

Copy Highlighter-hljs
[2,2,2,0]

输出:

Copy Highlighter-hljs
0

样例四#

最小值是数组的第一个或最后一个元素。

Copy Highlighter-hljs
[1,2,1]

输出:

Copy Highlighter-hljs
1

样例五#

数组旋转的元素个数是 0 个,也就是传了原始的数组进来。

Copy Highlighter-hljs
[1,2,3]

输出:

Copy Highlighter-hljs
1

样例六#

数组中只出现过一个数字。

Copy Highlighter-hljs
[1,1]

输出:

Copy Highlighter-hljs
1

遍历查找#

解题思路#

这个做法没什么好说的,就是直接遍历一遍找到数组的最小值。虽然不能减少 O(n),但是可以减少 T(n)。观察上述的样例,显然当 nums[i] < nums[i-1] 时 nums[i] 就是数组的最小值。如果用这种方式找不到最小值,说明传入的数组的旋转元素个数为 0(对应样例五、六),这时应该返回第一个元素。

题解代码#

Copy Highlighter-hljs
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 减小解空间的范围,其实换成遍历查找也是减治法。

题解代码#

Copy Highlighter-hljs
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. 旋转数组的最小数字(二分法,清晰图解)

posted @   乌漆WhiteMoon  阅读(77)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
历史上的今天:
2020-07-28 链路层:MAC 地址
点击右上角即可分享
微信分享提示
CONTENTS