(一)题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
(二)思路分析
直观的解法:是从头到尾遍历一次数组,找到最小数字,时间复杂度O(n)。没有利用旋转数组的特性,PASS。
优秀的解法:由于本题给出的数组在一定程度上是有序的,可以通过二分法进行查找。时间复杂度O(logn).
(1)旋转之后的数组可以划分为两个排序的子数组,而且前面的子数组元素大于或者等于后面子数组的元素。最小的元素刚好是这两个数组的分界线。
(2)定义两个指针p1、p2,分别指向数组的第一个元素和最后一个元素。p1总是指向前面递增数组的元素,p2总是指向后面递增数组的元素;
(3)找到中间的元素,如果该元素大于或者等于p1,则该元素位于前面的递增子数组中,如(2 3 4 5 1);同理如果中间元素小于或者等于p2,则该元素位于后面的递增子数组中,如(4 5 1 2 3)。
(4)p1向后跑,p2向前跑,最终p1指向前面数组的最后一个元素,p2指向后面数组的第一个元素,也就是它们会指向两个相邻的元素,而第二个指针指向的刚好是所求的最小元素,这就是循环终止的条件。
特殊情况:
(1)旋转0个元素。此时数组的最小元素就是第一个数字。解决方法就是将mid初始化为第一个元素。
(2)p1、p2、mid三个的值相等,如(1 1 1 0 1或1 0 1 1 1)。采用顺序查找的方法。
(三)代码实现
#include<iostream>
#include <vector>
using namespace std;
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray)
{
if (rotateArray.size() == 0)
{
return -1;
}
int left = rotateArray[0];
int right = rotateArray.size() - 1;
int mid = left;
while (rotateArray[left] >= rotateArray[right]) //数组的左边值大于右边值
{
if (right - left == 1) //数组中只有两个元素
{
mid = right;
break;
}
mid = (left + right) / 2; //数组中超过两个元素
if (rotateArray[left] == rotateArray[right] && rotateArray[mid] == rotateArray[left]) //三个值相等
{
return minInOrder(rotateArray); //进行顺序查找
}
if (rotateArray[mid] >= rotateArray[left]) //中位数大于等于左边数
{
left = mid;
}
else if (rotateArray[mid] <= rotateArray[right]) //中位数小于等于于右边数
{
right = mid;
}
}
return rotateArray[mid];
}
int minInOrder(vector<int> rotateArray) //顺序查找
{
int min = rotateArray[0];
for (int i = 0; i<rotateArray.size(); ++i)
{
if (rotateArray[i] < min)
min = rotateArray[i];
}
return min;
}
};
int main()
{
Solution sol;
vector<int> rotateArray1 = {5,6,1,2,3,4};
vector<int> rotateArray2 = {1,1,1,0,1};
cout << sol.minNumberInRotateArray(rotateArray1) << endl; //1
cout << sol.minNumberInRotateArray(rotateArray2) << endl; //0
return 0;
}