题目:把一个数组最开始的若干元素搬到数组末尾生成新的数组,将该数组称为旋转数组。输入一个递增排序数组的一个旋转数组,输出旋转数组的最小元素。例如:数组{3,4,5,1,2,}为{1,2,3,4,5}的一个旋转数组,该数组的最小值为1;
思路1:顺序查找,时间复杂度o(n);
实现:
int GetMin(vector<int>& numbers)
{
int len = numbers.size();
if (len < 0)
{
throw exception("input invalid!");
}
int res = numbers[0];
for (int i = 1; i < len; i++)
{
if (res > numbers[i])
{
res = numbers[i];
}
}
return res;
}
思路2:根据旋转数组的规则:
一般情况,第一个元素大于等于最后一个元素,采用二分查找有:
(1)设置两个指针P1和P2分别指向数组的第一个和最后一个元素;
(2)找到数组的中间元素,若中间元素大于等于第一个元素,则最小元素位于中间元素的后面;
(3)若中间元素小于等于最后一个元素,则最小元素位于中间元素的前面;
(4)重复上述过程,则第一个指针总是指向前面递增数组的元素,第二个指针总是指向后面递增数组的元素,当P1和P2指向两个相邻的元素时,P2指向的就是最小元素。
实现
int GetMin(vector<int>& numbers)
{
int index1 = 0;
int index2 = numbers.size() - 1;
int minIndex;
while (numbers[index1] >= numbers[index2])
{
if (index2 - index1 == 1)
{
minIndex = index2;
break;
}
minIndex = index1 + (index2 - index1) / 2;
if (numbers[minIndex] >= numbers[index1])index1 = minIndex;
if (numbers[minIndex] <= numbers[index2])index2 = minIndex;
}
return numbers[minIndex];
}
在思路2中有两种特殊情况,分别是:
(1)原数组可看成将前0个元素搬到数组末尾的旋转数组,则第一个元素小于等于最后一个元素,此时,最小元素就为第一个元素,因此解决方案为初始化中间元素下标为第一个元素下标。
(2)在一般情况中,当第一个元素、中间元素、最后一个元素三者相等时,认为最小元素位于中间元素的后面。但实际情况并非如此。比如:数组{1,0,1,1,1}是数组{0,1,1,1,1}的旋转,此时,第一个元素、中间元素、最后一个元素的值都为1,但最小元素位于中间元素的前面;当出现这种情况时,采用顺序查找确定最小元素。
实现
int GetMin(vector<int>& numbers)
{
int index1 = 0;
int index2 = numbers.size() - 1;
int minIndex = index1; //特例1的解决方案。
while (numbers[index1] >= numbers[index2])
{
if (index2 - index1 == 1)
{
minIndex = index2;
break;
}
minIndex = index1 + (index2 - index1) / 2;
if (numbers[index1] == numbers[index2] && numbers[index1] == numbers[minIndex]) //特例2的解决方案
{
return minInorder(numbers, index1, index2);
}
if (numbers[minIndex] >= numbers[index1])index1 = minIndex;
if (numbers[minIndex] <= numbers[index2])index2 = minIndex;
}
return numbers[minIndex];
}
int minInorder(vector<int>& numbers, int index1, int index2)
{
int res = numbers[index1];
for (int i = index1 + 1; i <= index2; i++)
{
if (res > numbers[i])
{
res = numbers[i];
}
}
return res;
}