把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
示例 1:
输入:[3,4,5,1,2]
输出:1
示例 2:
输入:[2,2,2,0,1]
输出:0
题目分析
由题意可得,旋转数组就是讲原数组分为左右两部分单独进行旋转后得到的数组,具体如下图所示,寻找旋转数组的最小元素即为寻找右排序数组的首个元素numbers[x],称x为旋转点 。
根据题意,我们第一时间就能通过暴破来解决,用一个变量记录一下当前遍历过程中遇到的最小值是多少,然后遍历结束后,返回最小值即可。但是暴破不是我们的目标,我们的目标是降低算法的复杂度,所以这里采用二分法解决,可将其遍历法的线性级别时间复杂度降低至对数级别 。
算法过程
首先,创建两个指针left、right ,分别指向 numbers的首尾数字,然后计算出两指针之间的中间索引值 numbers[middle],然后我们会遇到以下三种情况:
- numbers[middle] > numbers[right] :代表最小值一定在 middle 右侧,所以将 left 移到 middle + 1的位置。
- numbers[middle] < numbers[right] :代表最小值一定在 middle 左侧或者就是 middle,所以将 right 移到 middle 的位置。
- numbers[middle] 既不大于 numbers[left] 指针的值,也不小于 numbers[right] 指针的值,代表着 middle 可能等于 left 指针的值,或者 right 指针的值,我们这时候只能让 right 指针递减,对数组遍历找最小值了。
代码如下
/**
* @param {number[]} numbers
* @return {number}
*/
var minArray = function(numbers) {
//用二分查找法
let left=0,right=numbers.length-1;
while(left<right){
//~~按位取反,连取两次反,得到整数
let middle=left+ ~~((right-left)/2);
//如果中间的值小于右边的值,那么最小值应该是left~middle区间
if(numbers[middle]<numbers[right])right=middle;
//如果中间的值大于右边的值,那么最小值应该是middle+1~right区间
else if(numbers[middle]>numbers[right])left=middle+1;
//否则,最小值无法判断在哪个位置,只能对数组进行遍历一个一个找
else right--;
}
return numbers[left];
};