题目
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组
[3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
题目链接:
剑指 Offer 11. 旋转数组的最小数字
来源:力扣(LeetCode)
涉及的知识点:二分查找
求解思路
题目所给旋转数组的特点:
设数组的最后一个元素为x,该数组最小的元素为min。原数组中,min左边的所有元素均大于等于x,min右边的所有元素均小于等于x。 根据这一特点,我们可以得出,利用二分查找,一定可以找到min。
二分查找
本题,我们将二分查找过程中的左边界设为left,右边界设为right,区间的中点设为mid。其中,初始化left为0,right为numbers.length-1
。数组的最小值min一定在该区间内。
在对区间进行更新前,我们需要计算区间的中点,接着判断numbers[mid]
和numbers[right]
的大小关系,共有三种情况,分别进行不同的区间更新操作,直至找到最小值:
- numbers[mid] < numbers[right]
则令right = mid - numbers[mid] > numbers[right]
则令left = mid+1
【备注:mid+1保证了左边界不会因为原地踏步而造成死循环】 - numbers[mid] == numbers[right]
相等情况,我们无法保证min到底是在mid的左边还是右边,则进行right--
操作
最终,当left不再小于right时,说明我们找到了最小值,并返回最小值。
AC代码
class Solution {
public int minArray(int[] numbers) {
int left = 0;
int right = numbers.length-1;
while(left < right) {
int mid = left+(right-left)/2;
if(numbers[mid] < numbers[right]) {
right = mid;
}else if(numbers[mid] > numbers[right]){
left = mid+1;
}else {
right--;
}
}
return numbers[left];
}
}
细节说明
这里mid的更新代码为int mid = left+(right-left)/2
,可以避免(left+right)/2
因left
和right
的数值太大,造成溢出。