把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
思路一:顺序查找
如果数组旋转后,数组可分为两部分,前部分数字是非递减,后面元素也是非递减,从第二个元素开始判断,如果后一个元素(B)比前一个元素(A)小,那么后一个元素(B)则是旋转数组里最小元素。那么这样时间复杂度就是0(n)。代码如下:
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
if (rotateArray.size() == 0)
return 0;
int i = 0;
while (i < rotateArray.size()-1)
{
//如果前一个数字比后一个大,那么后一个数字肯定是最小元素
//如果前一个数字小于等于后一个数字,那么最小数字可能是该数字或者后面数字
//等于情况 3 4 4 5 1
if (rotateArray[i] <= rotateArray[i + 1])
i++;
else // 3 4 5 1 直接返回[2+1]
return rotateArray[i+1];
}
// 3 3 3 3 3 循环出来后,i=4,可以返回[4],也可以返回[3],但是返回[3]空间复杂度小
return rotateArray[i-1];
}
};
思路二:二分查找
由于数组分成的两部分都是有顺序的,并且是查找最小数字,可以选择用二分查找。
首先left=0,right=size()-1,因为旋转后,左边元素大于等于右边元素,左边第一个元素大于等于右边元素,即如果[mid]>=[left],那么说明最小元素在mid右边,那么left=mid;如果[mid]<[left],说明最小元素在mid左边,那么right=mid。那么left一直指向左边元素,right一直指向右边元素,当right-left=1时,即他们指向相邻的元素,right指向元素刚好是最小元素,就跳出循环。但是这种方法只能解决没有重复数字,但是如果是下面情况,二分查找将不适用:
代码如下:
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
int len = rotateArray.size();
if (len == 0)
return 0;
int left = 0;
int right = len - 1;
int mid = 0;
while (rotateArray[left] >= rotateArray[right])
{
mid = left + (right - left) / 2;
if (right - left == 1)
{
mid = right;
break;
}
if (rotateArray[left] == rotateArray[mid] && rotateArray[mid] == rotateArray[right])
{
//顺序排序
int i = left;
while (i<right)
{
if (rotateArray[i] <= rotateArray[i + 1])
i++;
else
return rotateArray[i + 1];
}
return rotateArray[i];
}
else { //二分查找
if (rotateArray[left] <= rotateArray[mid])
left = mid; //最小元素在mid右边,需要是left=mid,不能是mid+1,因为mid可能是最小元素
else
// 如 3 4 5 1 2 ,当left=2,right=4,mid=3,[mid]是最小元素,所以需要是right=mid,而不是right=mid+1
right = mid;
}
}
return rotateArray[mid];
}
};