把一个数组最开始的若干个元素搬到数组的末尾,咱们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的全部元素都大于0,若数组大小为0,请返回0。web
对于非减数组来讲,数组右边的元素必定大于等于数组左边的元素。当对非减数组进行旋转后(把数组最开始的元素搬到末尾),则在遍历过程当中可能会出现右边的元素反而小于左边的元素,当第一次出现这种状况时,必定是原非减数组的开头,即整个数组的最小元素。算法
public int minNumberInRotateArray(int[] rotateArray) { if (rotateArray.Length <= 0) { return 0; } for (int i = 1; i < rotateArray.Length; i++) { if (rotateArray[i - 1] > rotateArray[i]) { return rotateArray[i]; } } return rotateArray[0]; }
解法1是顺序遍历数组找到最小值,这样的时间复杂度是O(n),那么有没有什么办法进行优化呢?
本题要查找的是非减排序数组的旋转数组的最小值。实际上是有必定顺序的,对于有序数组的查找,咱们天然想到了二分查找。
先介绍一下二分查找的基本思想:
首先,假设数组中元素是按升序排列,将数组中间位置元素与要查找的元素比较,若是二者相等,则查找成功;不然利用中间位置索引将数组分红左、右两个子数组,若是中间位置的元素大于要查找的元素,则进一步查找左子数组,不然进一步查找右子数组。重复以上过程,直到找到知足条件的元素,使查找成功,或直到子数组不存在为止,此时查找不成功。
二分查找的时间复杂度是O(log2n)数组
以数组arr = {3,4,5,1,2}为例,能够分红两个有序非减数组来看待,以下图所示
显然,数组的最小值就在两个非减数组的交界处,同时因为arr是一个非减数组旋转获得的,因此左边数组的最小值必定大于等于右边数组的最小值。利用二分查找,使左边的指针指向索引0即3,右边的索引指向索引4即2,求得mid = low + (high - low)/2 = 2
,比较arr[mid]和arr[high]的值(这里说明为何不使用arr[mid]和arr[low]进行比较,由于按照上面的算法,mid有可能等于low,再比较arr[mid]和arr[low]没有意义)svg
若是是求一个递增数组的旋转数组的最小值,则上述逻辑已经足够,但本题是求非减数组的旋转数组的最小值,也就是说可能存在两个元素相等的状况。
好比旋转数组{1,0,1,1,1}和{1,1,1,0,1}均可以当作非减数组{0,1,1,1,1}的旋转数组
此时对于它们而言arr[mid] = arr[high],这种状况下咱们并不知道mid是在最小值的左边仍是右边,好比这两个旋转数组,一个是在mid的左边,一个反而在mid的右边。当出现这种状况时咱们能够认为数组的有序性丢失了,不能再继续使用二分查找,而只能顺序遍历从low到high找到最小值,即high = high - 1或者low = low + 1。优化
public int minNumberInRotateArray(int[] rotateArray) { if (rotateArray.Length <= 0) { return 0; } int low = 0, high = rotateArray.Length - 1; while(high > low) { int mid = low + (high - low) / 2; if (rotateArray[mid] > rotateArray[high]) { low = mid + 1; }else if (rotateArray[mid] < rotateArray[high]) { high = mid; }else { high = high - 1; } } return rotateArray[low]; }
在想到用二分查找优化本题的时候,其实遇到了问题,就是上面有提到的当中间元素等于高位元素时,不知道应该左移仍是右移的问题。一度以为这道题可能用二分查找解不了,后来看到某个大神的代码,才恍然大悟,这种状况下退化成顺序查找就能够。想不到这种方法的缘由仍是太执着于二分查找的标准形式。一直是在套用算法,而没有想到变通,或融合其余算法。spa