旋转数组的最小数字

Posted 做1个快乐的程序员

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了旋转数组的最小数字相关的知识,希望对你有一定的参考价值。

旋转数组的最小数字(点击链接进入题目)

描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
示例1
  输入:[3,4,5,1,2]
  返回值:1

法一:线性遍历,定义一个min变量用来存储最小值,然后遍历数组,遇到小的就更新min,最后输出min。代码较简单,但是时间复杂度为O(N)。不推荐。

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        int min = rotateArray[0];
        for(const auto& val : rotateArray)
        {
            if(val < min)
                min = val;
        }
        return min;
    }
};

法二:定义两个变量slow和fast,指向数组的前两个元素,依次比较slow和fast,如果slow <= fast,则slow++,fast++,如果遇到slow > fast,则fast就是我们要找的值,这个和法一 一样,没有优化多少,遇到极端情况,比如最小的数在最后面,也是O(N)级别的。

但是当数组是如下情况时,就会出现问题。所以我们加入一个标志位flag,当slow和fast一直往后移,仍没有slow>fast时,就输出数组的第一个元素。

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        if(rotateArray.size() == 0)
            return 0;
        
        int flag = 0;
        int slow = 0;
        int fast = slow + 1;
        while(fast < rotateArray.size())
        {
            if(rotateArray[slow] <= rotateArray[fast])
            {
                slow++;
                fast++;
                flag = 1;
            }
            else
                return rotateArray[fast];
        }
        if(flag != 0)
            return rotateArray[0];
        else
            return 0;
    }
};

法三:根据法二的分析,我们知,不管我们怎样旋转,一定会将数组分为两部分,每一部分都是非递减的排序数组,而且前半部分数组的值整体大于后半部分,而我们要找的结果在数组中间位置的左边或者右边所以本题我们可以借鉴二分查找,定义一个start和end,在定义一个int mid = (start + end)>>1。我们旋转的深或者浅,最小的值要么在mid左边要么在mid右边,要么就是mid,如果arr[mid] < arr[start],说明arr[mid]是属于后半部分数组的,所以要找的值在前半部分数组,right = mid,如果arr[mid] >= arr[start],说明arr[mid]和arr[start]共同属于前半部分数组的,所以要找的值在后半部分数组的开头,left = mid,这个过程,会让[left, right]区间缩小

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        //如果数组的大小为0,则返回0
        if(rotateArray.empty())
            return 0;
            
        //定义start和end,分别指向数组的最左边和最右边,并定义一个mid变量表示中间值
        int start = 0;
        int end = rotateArray.size() - 1;
        int mid = 0;
        //只有当数组最左边的元素大于最右边的元素时,才进入循环
        //如果不满足就对应法二中的图二的情况,此时直接返回数组中首位置的元素
        while(rotateArray[start] >= rotateArray[end])
        {
            //如果只有1个元素应该怎么办,只有一个元素,说明该值即最右边的元素就是我们要找的值
            if(end - start == 1)
            {
                mid = end;
                break;
            }
            
            //计算mid
            mid = ((start + end) >> 1);
            
            //如果mid、left、right三个位置上的值相等,我们是无法区分左右两个数组的
            //此时我们只能通过线性的方法,遍历整个数组去寻找
            if(rotateArray[mid] == rotateArray[start] && rotateArray[start] == rotateArray[end])
            {
                //直接遍历去找
                int ret = rotateArray[start];
                for(int i = start + 1; i < end; i++)
                {
                    if(ret  > rotateArray[i])
                        ret = rotateArray[i];
                }
                return ret;
            }
            
            //如果arr[mid] >= arr[start],说明arr[mid]和arr[start]共同属于前半部分数组的
            //所以要找的值在后半部分数组的开头,start= mid
            if(rotateArray[mid] >= rotateArray[start])
                start = mid;
            //如果arr[mid] < arr[start],说明arr[mid]是属于后半部分数组的
            //所以要找的值在前半部分数组,end= mid
            else
                end = mid;
        }
        return rotateArray[mid];
    }
};

以上是关于旋转数组的最小数字的主要内容,如果未能解决你的问题,请参考以下文章

《剑指offer》— JavaScript旋转数组的最小数字

旋转数组的最小数字

剑指 Offer 11. 旋转数组的最小数字 的详细题解

剑指offer旋转数组的最小数字

剑指offer——旋转数组的最小数字

剑指OFFER旋转数组的最小数字