⭐️前面的话⭐️
大家好!博主开辟了一个新的专栏——剑指offer,我要开始刷题了!这个专栏会介绍《剑指offer》书上所有的面试编程题。并且会分享一些我的刷题心得。由于博主水平有限,如有错误,欢迎指正,如果有更好的解题思路和算法可以分享给博主哦!一起加油!一起努力!
📒博客主页:未见花闻的博客主页
🎉欢迎关注🔎点赞👍收藏⭐️留言📝
📌本文由未见花闻原创,CSDN首发!
✉️坚持和努力一定能换来诗与远方!
💭参考书籍:📚《剑指offer第1版》,📚《剑指offer第2版》
💬参考在线编程网站:🌐牛客网🌐力扣
🙏作者水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!
博主的码云gitee,平常博主写的程序代码都在里面。
⭐️剑指 Offer 11. 旋转数组的最小数字⭐️
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
💡示例
输入:[3,4,5,1,2]
输出:1
输入:[2,2,2,0,1]
输出:0
💡限制
无
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof
⭐️解题思路⭐️
这道题输入的数组2个特点:
- 数组是经一个增序数组旋转转一次得来的,如果有元素旋转到数组末尾,这个旋转后的数组会有两段递增序列。比如[1,2,3,4,5]旋转后得到[3,4,5,1,2],里面有
3,4,5
与1,2
两个递增序列。如果没有有元素旋转到数组末尾,那旋转后的数组与旋转前的数组相同。 - 旋转后的数组的
左递增序列中所有的元素
会大于或等于右递增序列中所有的元素
,我们不妨称数组中最小的元素的下标为对称点
。
根据这个特点我们可以发现旋转后的数组会有一个断层
现象,就是这个数组第一个递增序列的最后一个元素会比第二个递增首个元素要大,出现了一次递减现象。所以我们不妨根据这个特点作为突破口。当然如果这个旋转后的数组与旋转前一模一样,那它的第一个元素就是数组中最小的元素。
方法1: 线性遍历
。(用此方法没有达到面试官的期望)
即对数组直接遍历,当发现相邻两个元素中前面一个元素比后面一个元素大,则后面那个较小的元素就是数组中最小的元素。否则,数组中第一个元素就是最小元素。
时间复杂度: O(N)
除了直接对数组暴力遍历还有一个更优的方法,就是利用二分的思想对数组进行查找,去找到数组中最小的元素。能把时间复杂度缩小到对数级。那么问题来了,应该使用数组中哪一个元素与中间的元素进行比较呢?二分的思想是每进行一次比较就能缩小一半的范围。显然选择数组的两端进行比较是最合理的,可以以右端点为基准也可以以左端点为基准。
设左端点下标为left
,右端点下标为right
,中点下标为mid
,对称点下标为x
,数组地址为arr
,数组元素个数为n
。
方法2: 以左端点或右端点为基准的二分查找
(最优解)
初始化:left=0
; right=n-1
; mid=(left+right)/2
或 mid=left+(right-left)/2
后者能防止left+right
数据溢出。
循环二分:循环条件为left < right
。
如果我们以左端点为基准进行二分查找,会出现一个问题,就是当*(arr + mid) > *(arr + left)
时,有两种情况,一是数组旋转前后不相同,则最小元素在区间[m,right]
,二是数组旋转前后相同,则最小元素就是数组第一个。这样就不能确定区间了,所以我们采用以右端点为基准的方法进行二分查找。
当*(arr + mid) > *(arr + right)
时,最小元素在区间[mid + 1,right]
,所以可以使left=mid+1
。
当*(arr + mid) < *(arr + right)
时,最小元素在区间[left,mid]
,所以可以使right=mid
。
当*(arr + mid) = *(arr + right)
时,并不能确定一个区间,有两种方法解决这个问题,一是将[left,right]
区间内的元素进行遍历,求最小元素;二是使right--
。在此方法中我们采用后者。
为什么将右端点right--
就能满足呢?
我们分为分为两种情况来讨论:
当 x < right
时: 在执行 right