已知一个长度为 n
的数组,预先按照升序排列,经由 1
到 n
次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7]
在变化后可能得到:
- 若旋转
4
次,则可以得到[4,5,6,7,0,1,2]
- 若旋转
7
次,则可以得到[0,1,2,4,5,6,7]
注意,数组 [a[0], a[1], a[2], ..., a[n-1]]
旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]]
。
给你一个元素值 互不相同 的数组 nums
,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
要求:求最小元素
分析题目:旋转4次 可以得到【4,5,6,7,0,1,2】
不难发现:
数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。
我的想法是 想象成一个逆时针转动的圆盘
每一次转动 都会把数字最后面的元素提到最前面来
二分查找法 时间复杂度为 O(log n)
思路:在这里我们使用二分查找法,始终将最小值套住,去不断的收缩左边界或者右边界
分别去讨论左中右的情况:
左 <中<右:
没有发生旋转的情况,其中最小值依然是按顺序排列在最左边,这时候我们可以将右边界进行收缩。
左 >中>右:
逆序排列,单调递减,在3个元素以上是不可能出现这种情况的,所以pass掉
左 <中>右:
中间的最大,最小值在右边,收缩左边界,这里可以直接让left = mid+1
左 >中<右
中间的最小,最小值在左边,收缩右边界,但是不能判断mid所在的值是否为最小,所以不能把mid 直接忽略,让right = mid;即可
这里我们通过观察不难发现,其实归根结底只有两种情况,就是中<右 和 中>右(1和4类情况是一样的都是最小值在左边)
所以我们仅仅通过比较左值和右就可以确定收缩的方向
中间位置:将中间位置mid定义为int类型
所以mid会更接近left,这样解决了mid不是整数的问题,浮点数在计算过程中可能会出现精度丢失或溢出的情况,影响到二分查找的正确性
而且,可以确保 mid 更接近左边,从而有利于判断最小值所在的区间:原先的数是按照大小顺序排序的,左边的更小,右边的更大,要查找最小值,偏往左边寻找无疑是更有利于查找的
我们选择mid与right比较,为什么不和left去比较,刚开始这个问题也困扰了我良久,还是得结合前面的情况分析,跟右边比较无疑是更简单的,跟左边比较更适合用来寻找最大值,我们也可以转换思路,先找最大值,那紧挨的下一位肯定就是最小值(数组长度范围内,如果超出了就是a[0]最小值)
下面讨论一下循环条件while中的left <right
即当左右边界重合的时候,循环结束
学习算法的同时分享一些学习感想,如有错误,还希望大佬及时指出