题目要求
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
解题思路
实际上这道题并不难,只需要遍历整个数组就可以知道最小的数字是什么。但是,该方法的时间复杂度O(n)没有很好的利用到旋转数组的特性,所以可能不会达到面试官的要求。
我们观察一下旋转之后的数组:{3,4,5,1,2}实际上是两个递增的子序列{3,4,5}和{1,2}并且前面子序列的元素都大于或者等于后面子序列的元素。关键:最小的元素刚好是两个子序列的分界点。在排序的数组中,我们通常使用二分查找法实现O(logn)的查找。所以我们给出如下的步骤:
(1)设置两个指针,分别指向第一个子序列的第一个元素和第二个子序列的最后一个元素。
(2)接着我们找到,数组的中间元素(如上述中的5),这里有两种情况,一种是在第一个子序列的最后一位,另一种是第二个子序列的第一位。我们可以根据情况来调整指针1或者指针2。以此来减少寻找的范围。
(3)无论是(2)中那种的移动方法,都会缩小寻找的范围,最终指针1会指向第一个子序列的最后一个元素,指针2会指向第二个子序列的第一个元素。(就是指针1和指针2相邻),而最小的元素就是指针2指向的元素。到这里程序就算结束了。
这里边有个问题:
当旋转数组和原数组一样的时候,即旋转了原数组的前0个元素,那么此时无法计算中间元素,就会报错,所以我们在程序里初始化中间元素的值为数组首元素来避免这个问题。
这里边的重点:
找到中间元素是位于第一个子序列还是第二个子序列?
终止循环的条件是什么:当指针1和指针2相邻,数组索引差1的时候。
主要代码 c++
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
if(rotateArray.size()==0) return 0; //若数组大小为0,返回0
int index1 = 0;
int index2 = rotateArray.size() - 1;
int mid = index1; // 防止旋转数组和原数组一致(只旋转0个元素)
while(rotateArray[index1] >= rotateArray[index2])
{
if(index2-index1==1) // 停止条件
{
mid = index2; // 最小元素是第二个子序列的首元素
break;
}
mid = (index1 + index2) / 2; // 寻找中间元素
if(rotateArray[index1]<=rotateArray[mid]) //一定要有等号
index1 = mid;
else if(rotateArray[index2]>=rotateArray[mid])
index2 = mid;
}
return rotateArray[mid];
}
};
总结
实际上二分查找的思想就是根据不断调整指针1和指针2的值来减小搜索的范围,直到两个指针相遇,停止循环。