【剑指Offer】旋转数组的最小数字

题目描述

把一个数组最开始的若干个元素搬到数组的末尾,咱们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的全部元素都大于0,若数组大小为0,请返回0。web

解法1

对于非减数组来讲,数组右边的元素必定大于等于数组左边的元素。当对非减数组进行旋转后(把数组最开始的元素搬到末尾),则在遍历过程当中可能会出现右边的元素反而小于左边的元素,当第一次出现这种状况时,必定是原非减数组的开头,即整个数组的最小元素。算法

实现代码

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)数组

解法2

以数组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

  • 若是arr[mid] > arr[high],则说明当前的mid,处于左边的非减数组中,则最小值在mid的右边,则将low指向mid +
    1
  • 若是arr[mid] < arr[high],则说明当前的mid,处于右边的非减数组中,则最小值在mid的左边,则将high指向mid(这里说明为何high不指向mid - 1,一样由于mid的算法,可能存在mid = low = 0,若是high = mid - 1,则high有可能小于0,为了不这种状况的判断,因此采用high = mid )

若是是求一个递增数组的旋转数组的最小值,则上述逻辑已经足够,但本题是求非减数组的旋转数组的最小值,也就是说可能存在两个元素相等的状况。
好比旋转数组{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

相关文章
相关标签/搜索