一,题目
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
二,分析
最容易想到的方法是从头到尾遍历一遍,时间复杂度为O(n)。
但是我们并没有利用旋转数组本身的性质,发现原数组可以划分为两个排序的子数组,而且前面的子数组元素都大于或等于后面的子数组。而且我们要找的最小元素正好是这两个子数组的分界线。在排序的数组中我们可以用二分查找实现O(logn)查找。所以我们可以试着用二分查找的思路来寻找最小的元素。
我们举一个简单的例子,如数组{3,4,5,1,2}是排序数组{1,2,3,4,5}的旋转数组。用两个指针分别指向第一个元素和第5个元素。位于两个指针中间的数字是5,它大于第一个指针指向的数字,因此中间数字5一定位于第一个递增子数组中,并且最小的数字一定位于它的后面。因此我们可以移动第一个指针让它指向数组的中间。
此时位于两个指针中间的数字是1,它小于第二个指针指向的数字,因此中间数字1一定位于第二个递增子数组中,并且最小的数字一定位于它的前面或者本身就是最小数字。
以此类推,当两个指针的距离是1,表明第一个指针已经指向了第一个递增子数组的末尾,而第二个指针指向第二个递增子数组的开头,也就是最小的数字。
三,实现
基于上述分析,可以写出如下代码:
#include "stdafx.h"
#include<iostream>
using namespace std;
int MinInorder(int* numbers, int index1, int index2)
{
int result = numbers[index1];
for(int i = index1 + 1; i <= index2; ++i)
{
if(result > numbers[i])
result = numbers[i];
}
return result;
}
int Min(int *numbers, int length)
{
if(numbers == NULL || length <= 0)
{
cout<<"invalid parameters"<<endl;
return NULL;
}
int index1 = 0, index2 = length - 1;
int indexMid = index1;
while(numbers[index1] >= numbers[index2])
{
if(index2 - index1 == 1)//循环结束条件
{
indexMid = index2;
break;
}
indexMid = (index1 + index2)/2;
if(numbers[index1] == numbers[index2] && numbers[index1] == numbers[indexMid])
return MinInorder(numbers, index1, index2);
else if(numbers[indexMid] >= numbers[index1])
index1 = indexMid;
else if(numbers[index2] >= numbers[indexMid])
index2 = indexMid;
}
return numbers[indexMid];
}
int main()
{
int a;
//int data[5] = {3,4,5,1,2};//一般旋转数组
//int data[] = {1,0,1,1,1};//特殊情况
//int data[] = {1,1,1,0,1};
//int data[] = {6,6,7,5,5};
a = Min(data,5);
cout<<a<<endl;
system("pause");
return 0;
}