题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
思路1:如果此类题目出现在在线编程题中,那么想要通过系统的测试数据,无疑是非常简单的,不啰嗦,直接贴代码:
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
//数组为空时
if(rotateArray.size() == 0)
return -1;
//前部分数据旋转
for(int i = 0; i < rotateArray.size() - 1; i++){
if (rotateArray[i] > rotateArray[i + 1])
return rotateArray[i + 1];
}
//全部数据旋转,相当于没有旋转,最小数即为第一个数
return rotateArray[0];
}
};
核心代码:第10行和第11行;旋转之后的数组由两个递增数组构成,如果发现了在原数组中a[i]>a[i+1],那么久可以判定最小值为a[i+1]了;
但是上述代码的时间复杂度为O(N)
,太慢,在面试的时候,如果给出上述实现,面试官必然不会满意!
思路2:
1. 分别用两个指针left,right
指向数组的第一个元素和最后一个元素;(按题目中的规则,第一个元素应该是≥最后一个元素的)
2. 找到数组的中间元素center=(left+right)/2
来取得;如果center
位于前面的递增子序列,那么它应该≥left
指向的元素;我们就可以将left
移至center
处,缩小寻找的范围;如果center
位于后面的递增子序列,那么它应该≤right
指向的元素;我们就可以将right
移至center
处,缩小寻找的范围;无论是移动哪一个指针,都会缩小范围,然后用更新之后的指针做新一轮的查找;
3. 最终第一个left
指向前面子数组的最后一个元素,right
指向后面子数组的第一个元素,也就是他们两指向相邻的两个元素,二第二个指针指向的刚好就是最小的元素.这就是循环的出口;
贴代码:
public class Solution {
public int minNumberInRotateArray(int [] array) {
if(array==null){
throw new NullPointerException();
}
int length=array.length;//数组的长度
int left=0;
int right=length-1;
int center=left;
while(array[left]>=array[right]){
if(right-left==1){
center=right;
break;
}
center=(left+right)/2;
if(array[center]>=array[left]){
left=center;
}else if(array[center]<=array[right]){
right=center;
}
}
return array[center];
}
}
注意:第一个数字总是大于最后一个数字,那么循环的条件就可以设置为while(array[left]>=array[right])
,但是有一个特例,也就是当把排序数组的前面0个元素搬到后面的时候,那么旋转数组就等于原数组,则此时上述条件是不成立的,所以应该直接return[0]
;这也就是为什么int center=left;
的原因(此时center==0
)!
上诉代码时间复杂度:O(logN);
万事大吉啦? .. 别高兴得太早…
考虑如下情况:
[1,0,1,1,1]和[1,1,1,0,1]都可以看做是数组[0,1,1,1,1]的旋转;
在第一个旋转数组中,array[left]=1;array[right]=1;array[center]=1;
在第二个旋转数组中:也有
array[left]=1;array[right]=1;array[center]=1;
但是在第一种情况中,a[center]位于后面的子数组中;在第二种情况中,a[center]位于前面的数组中;
所以当array[left]==array[right]&&array[left]==array[center]&&array[right]==array[center]
时,无法判断a[center]是位于前面的数组中还是后面的数组中,故无法移动指针,进而无法缩小范围.
当出现这种情况的时候,我们采用顺序查找来解决问题.
最终的代码如下所示:
public class Solution {
public int minNumberInRotateArray(int [] array) {
if(array==null){
throw new NullPointerException();
}
int length=array.length;//数组的长度
int left=0;
int right=length-1;
int center=left;
while(array[left]>=array[right]){
if(right-left==1){
center=right;
break;
}
//当下标left,right,center三个位置处的数相等的时候,则只能顺序查找
if(array[left]==array[right]&&array[left]==array[center]&&array[right]==array[center]){
return minInOrder(array,left,right);
}
center=(left+right)/2;
if(array[center]>=array[left]){
left=center;
}else if(array[center]<=array[right]){
right=center;
}
}
return array[center];
}
int minInOrder(int[]array,int left,int right){
int result=array[left];
for(int i=left+1;i<=right;i++){
if(result>array[i]){
result=array[i];
}
}
return result;
}
}