今天同事问我一道算法题,题目是这样:
给定一个递增循环整数数组,从里面找出最小的元素,使用的算法越快越好。特别地,最小的元素可能出现在数组中间。比如:50, 52, 63, 90, 3, 8, 15, 44。
解析:
这里有条件:1、递增。2、循环
对于有序的数组,二分搜索一般是解决此类问题的利器,可以将时间复杂度从O(n)提高至O(log(n))。可这里却有一个较为苛刻的条件,是循环递增。
二分搜索的核心思想:利用现有的条件,每一次的搜索,都可以将目标的范围缩小一半,从而实现加速查找的效率。我们不用死守着最基本那一套。
这里把可能的趋势图画一下:
1、最低------------------最高(这里最特殊的一种情况)
这里情况最好办了,直接判断最后一个是否大于第一个元素的值即可判断。
2、大低----------大高,最低----------小高(这是最普遍的情况)
因为是循环递增,所以以最小值为界,左半边的值,全部都大于右半边。二分搜索的关键在于,不停在向目标点(最小值靠近)
算法:
初始化left=array[0], right = array[len-1];
每次取中点值mid = (left + right) / 2;
while (left + 1 < right ) //这里考虑到只有两个值情况下,那就返回array[right],或者本身right所在位置就是最小值,不停搜索之后,直接返回array[right]即是答案
{
如果array[mid ] 比它前面一个值要小,那说明它便是最小值了,算法退出,返回最小值。
如果arrary[mid] < arrary[right],那说明取得的mid位置落到了右半边,将右边界移到mid,继续下次二分搜索。
如果array[mid ] > arrary[left], 说明mid位置落到了左半边,那将左边界移到mid,继续下次二分搜索。
}
return array[right];
退出循环后,那right便是已经逼到了最小值位置了。
3、当最小值在最右边的时候。
得出算法出下所示:
int findmin( int array[], int count ) { //情况1 if (array[count - 1] > array[0]) { return array[0]; } int left = 0, right = count - 1; //情况2 while(right > left + 1) { int mid = (left + right) / 2; if(array[mid] - array[(mid-1+count)%count] < 0 ) return array[mid]; else { if(array[mid] - array[left] > 0) left = mid; else right = mid; } } //情况3时 return array[right]; }