【剑指Offer-查找和排序】面试题11:旋转数组中的最小数字
题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
思路
根据定义,可以把旋转数组a[]看做是两个递增数组的拼接,且前一个递增数组中的元素值大于等于后一个递增数组中的元素值。递增数组的查找可以通过二分查找来实现。类似于二分查找,首先设置两个指针idx1和idx2表示查找的范围为[idx1, idx2]。查找范围中间的元素为mid=(idx1+idx2)/2,如果a[mid]>=a[idx1],则说明mid在第一个递增数组中,因为第一个递增数组中的元素值大于第二个递增数组中的元素值,所以最小值在第二个递增数组当中,此时设置idx1=mid,缩小查找范围;如果a[mid]<=a[idx2],则说明mid在第二个递增数组当中,则最小值在mid的左边,所以设置idx2=mid来缩小查找范围。这样,第一个指针总是指向前一个递增数组中的元素,而第二个指针总是指向后一个递增数组中的元素,这两个指针最终会指向相邻的元素。所以算法的终止条件就是idx1+1==idx2,此时idx2指向的元素就是旋转数组的最小元素。
代码如下:
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
int idx1 = 0;
int idx2 = rotateArray.size()-1;
while(rotateArray[idx1]>=rotateArray[idx2]){
if(idx1+1==idx2)
return rotateArray[idx2];
int mid = (idx1 + idx2) /2;
if(rotateArray[mid]>=rotateArray[idx1])
idx1 = mid;
else if(rotateArray[mid]<=rotateArray[idx2])
idx2 = mid;
}
return rotateArray[idx1]; //此时说明整个数组就是递增的,第一个元素就是最小元素
}
};
在前面的代码中,当idx1和mid指向的元素相同时,我们认为最小数字在idx1的后面,这其实是不一定的。例如:
也就是说,当idx1、idx2和mid指向的元素都相同时,我们是无法确定最小的数字是位于前面的子数组还是后面的子数组,也无法移动指针来缩小查找范围。针对这种情况,应该使用顺序查找来在[idx1,idx2]之间查找最小值,改进后的代码如下:
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
int idx1 = 0;
int idx2 = rotateArray.size()-1;
while(rotateArray[idx1]>=rotateArray[idx2]){
if(idx1+1==idx2)
return rotateArray[idx2];
int mid = (idx1 + idx2) /2;
if(rotateArray[idx1]==rotateArray[mid]&&rotateArray[mid]==rotateArray[idx2]){
int ans = rotateArray[idx1];
for(int i=idx1+1; i<=idx2; i++){
if(ans>rotateArray[i])
ans=rotateArray[i];
}
return ans;
}
if(rotateArray[mid]>=rotateArray[idx1])
idx1 = mid;
else if(rotateArray[mid]<=rotateArray[idx2])
idx2 = mid;
}
return rotateArray[idx1];
}
};
两份代码在牛客网均可以通过。