把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
算法思想:我们用两个指针分别指向数组的第一个元素和最后一个元素。按照题目中旋转规则,第一个元素应该是大于或者等于最后一个元素的(这其实不完全对,还有特例,后面再加以讨论)。接着我们可以找到数组中间的元素。如果该中间元素位于前面的递增子数组,那么它应该大于等于第一个指针指向的元素。此时数组中最小的元素应该位于该中间元素的后面。我们可以把第一个指针指向该中间元素,这样可以缩小寻找的范围。移动以后的一个指针仍然位于前面的递增数组中。同样,如果中间元素位于后面的递增数组中,那么它应该小于或者等于第二个指针指向的元素。此时该数组中最小的元素应该位于中间元素的前面。我们可以把第二个指针指向该中间元素,这样也可以缩小查找的范围。移动后的第二个指针仍然位于后面的递增子数组中。不管是移动第一个指针还是第二个指针,查找范围都会缩小到原来的一半,接下来我们再用更新之后的两个指针,重复做新一轮的查找。
基于上述的算法思想,代码如下:
int Min(int* numbers,int length)
{
if(numbers==NULL || length<=0)
{
throw new exception("Invalid parameters");
}
int index1=0;
int index2=length-1;
int indexMid=index1;
while(numbers[index1]>=numbers[index2])
{
if(index2-index1==1)
{
indexMid=index2;
break;
}
indexMid=(index1+index2)/2;
if(numbers[indexMid]>=numbers[index1])
{
index1=indexMid;
}
else if(numbers[indexMid]<=numbers[index2])
{
index2=indexMid;
}
}
return numbers[indexMid];
}
注意:如果把排序数组的前面的0个元素搬到最后面,即排序数组本身,这仍然是数组的一个旋转,这仍然是数组的一个旋转,我们的代码需要支持这种情况。此时,数组中的第一个数字就是最小的数字,可以直接返回。这就是上面的代码中,我们把indexMid初始化为index1的原因。一旦发现数组中第一个数字小于最后一个数字,表明该数组是排序的,就可以直接返回第一个数字了。
我们再来看一个例子,数组{1,0,1,1,1}和数组{1,1,1,0,1}都可以看成是递增排序数组{0,1,1,1,1}的旋转。在这两种情况下,第一个指针和第二个指针指向的数字都是1,并且两个指针中间的数字也是1,这3个数字相同。在第一种情况中,中间数字位于后面的子数组中;在第二中2情况下,中间数字位于前面的子数组中。因此,当两个指针指向的数字及他们中间的数字相同时,我们无法判断中间的数字是位于前面的子数组中还是后面的子数组中,也就无法移动两个指针来缩小查找的范围。此时,我们只能用顺序查找的方法。
有了上述的讨论,代码如下:
int Min(int* numbers,int length)
{
if(numbers==NULL || length<0)
{
throw new exception("Invalid parameters");
}
int index1=0;
int 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[indexMid]==numbers[index1])
{
return MinInOrder(numbers,index1,index2);
}
if(numbers[indexMid]>=numbers[index1])
{
index1=indexMid;
}
else if(numbers[indexMid]<=numbers[index2])
{
index2=indexMid;
}
}
return numbers[indexMid];
}
int MinInOrder(int* numbers,int length)
{
int result=numbers[index1];
for(int i=index1+1;i<=index2;++i)
{
if(result>numbers[i])
{
result=numbers[i];
}
}
return result;
}