把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
给你一个可能存在 重复 元素值的数组 numbers ,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一次旋转,该数组的最小值为 1。
注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。
示例 1:
输入:numbers = [3,4,5,1,2] 输出:1
解题思路
有序数组中的查找,优先考虑二分法查找,二分法的关键在于如何通过比较缩小查找范围。
数组 可以分为左右两个数组——左数组和右数组,我们需要查找的就是右数组的第一个数。
所以本题的解题思路的就是通过判断处于哪个左数组还是右数组来缩小查找的范围,最后把范围缩小到右数组的第一个数,即可查找到数组中的最小数字。
那么,如何判断处于左数组还是右数组呢?
——只需要与^[1]^进行比较即可。
是右数组的最大值
右数组的所有数都,左数组的所有数都
如果说明处于右数组,如果说明处于左数组
现在我们将与进行比较,如果,就移动到处,如果,就移动到^[2]^处,通过这种方式即可完成最小值的二分查找。
算法流程
- 初始化:声明两个指针分别指向数组的两端
- 循环二分:与进行比较有以下几种情况
- ,说明位于左数组,右移到处
- ,说明位于右数组,左移到处
- ^[3]^,无法判断,
- 返回值:时,结束循环,此时指向的就是数组的最小值
正确性证明:
-
能否与 进行比较?
不能,虽然对于数组 看起来是可行的,但如果没有数字被旋转,即 这类纯有序数组,则所有 都大于 ,无法确定其处于哪个数组。
-
为什么 是移动到 处,而 是移动到 处?
因为 时, 可能就是我们要求的最小值。
-
时,为什么可以通过 缩小范围?
因为 时, 肯定不是右排序数组的第一个元素。
代码
class Solution {
public int minArray(int[] numbers) {
int left = 0;
int right = numbers.length - 1;
int mid;
while (left < right) {
mid = (left + right) / 2;
if (numbers[mid] > numbers[right]) {
left = mid + 1;
} else if (numbers[mid] < numbers[right]) {
right = mid;
} else {
right--;
}
}
return numbers[left];
}
}