出处——《剑指offer》
题目:把一个数组最开始的若干个元素搬到数组的末尾,称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,该数组的最小值为1。
1.用遍历数组的方法来实现
int MinInOrder( int num[], index1, index2 ) /*index1为数组起始位置,index2为数组终止位置*/
{
int result = num[ index1 ];
int i;
for( i = index1 + 1; i <= index2; i++ )
{
if( result > num[i] )
result = num[i];
}
return result;
}
算法复杂度O(n)
2.若能够使用二分搜索实现可以使算法复杂度为O(logn)
书中有详细的讲解,跟一般的二分搜索类似,但由于不是查找特定的数,而是要找到最小的数,因此要找到如何能够确定最小数的方法。根据旋转数组的特点,可以将其看作两个递增数组的组合,且左边数组的所有元素都大于右边数组,最小数所在的位置为右边数组的第一位。
设定三个指针i1,i2以及mid,使得i1始终处在左边数组中,i2始终处在右边数组中, mid为中间数,通过i1,i2,mid元素间的大小关系来逐步确定最小数的位置。
例如:若num[mid] >= num[i1]表明mid在左边数组中,那么令i1 = mid就可以使得i1趋近于左边数组末尾;若num[mid] <= num[i2]表明mid在右边数组中,此时则可令i2 = mid。
最终当i2 - i1 == 1时,i2就为最小元素的位置。
int Min( int num[], int length ) /*length为数组长度*/
{
int i1, i2, mid;
i1 = 0;
i2 = length - 1;
while( num[i1] >= num[i2] ) /*保证i1,i2处在不同的子数组中*/
{
if( i2 - i1 == 1 )
return num[i2];
mid = ( i1 + i2 ) / 2;
if( num[mid] >= num[i1] )
i1 = mid;
if( num[mid] <= num[i2] )
i2 = mid;
}
return num[i1]; /*完全递增的数组也是旋转数组的一种,当出现这种情况时,由于num[i1] < num[i2]使得循环不执行,此时返回数组首元素*/
}
不过上面的程序面对一些特例时会出现问题,如数组{ 1, 0, 1, 1, 1 },会出现num[i1] = num[i2] = num[mid] = 1的情况,此时循环中的两个if语句中的判断条件都为真,因此当第一次循环结束后就会出现i1 = i2 = mid的情况,使得循环无限执行。因此需要将这种情况考虑到函数中。
int Min( int num[], int length )
{
int i1, i2, mid;
i1 = 0;
i2 = length - 1;
while( num[i1] >= num[i2] )
{
if( i2 - i1 == 1 )
return num[i2];
mid = ( i1 + i2 ) / 2;
if( num[i1] == num[i2] && num[i1] == num[mid] ) /*出现i1, i2, mid三个元素都相等的情况则采用顺序搜索的方法*/
return MinInOrder( num, i1, i2 );
if( num[mid] >= num[i1] )
i1 = mid;
if( num[mid] <= num[i2] )
i2 = mid;
}
return num[i1];
}