11 旋转数组的最小数字

题目描述:

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

 注意这里的非减排序,说明不是严格递增的。存在{0,1,1,2,3,3,4}

 

测试用例:

 1)功能测试(输入的数组是升序排序数组的一个旋转,数组中有重复数字或者没有重复数字) 考虑到有重复数字的情况,易忘

 2)边界值的测试(输入升序排序数组如{1,2,3,4,5},只包含一个数字的数组)

 3)特殊输入测试(输入nullptr指针)

 

解题思路:

 1)循环遍历寻找最小值,时间复杂度O(n)

//最直观的解法,但是面试时不建议这么写。这种思路没有利用输入的旋转数组的特性。
class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        //循环遍历
        int arrSize = rotateArray.size();
        if(arrSize<=0)return 0;
        int minValue=rotateArray[0];
        for(int i=0;i<arrSize;i++){
            if(minValue>rotateArray[i])
                minValue = rotateArray[i]; 
        }
        return minValue;
    }
}; //34ms

根据旋转数组的特性(1,2,3,4,5)(3,4,5,1,2)(5,1,2,3,4),遍历时找到的第一个最小值,就是数组的最小值。因此添加break中断即可,无需在遍历后面的数字。

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        //循环遍历
        int arrSize = rotateArray.size();
        if(arrSize<=0)return 0;
        int minValue=rotateArray[0];
        for(int i=0;i<arrSize;i++){
            if(minValue>rotateArray[i]){
                minValue = rotateArray[i];
                break; //提前退出即可,无需在遍历后面的数字
            }
        }
        return minValue;
    }
};

2) 二分查找 O(logn) 并考虑旋转数组特性

本题中给出的数组是部分排序的,尝试使用二分查找。考虑数组 {3,4,5,1,2} 

分析:两个指针初始分别指向首尾,

[1] 当首元素<=尾元素时,数组是排序的,直接返回第一个元素即可。{1,2,3,4,5} 

[2] 当首元素>=尾元素时,获取中间元素:

      -- 若中间元素>=前指针,则中间元素属于前面的递增子数组。将前指针移动到中间位置。

      -- 若中间元素<=后指针,则中间元素属于后面的递增子数组。将后指针移动到中间位置。

      查询的范围缩小一半。

[3] 终止条件:由于第一个指针总是指向前面的递增数组,第二个指针总是指向后面的递增数组。因此它们最终会指向两个相邻的元素,而第二个指针刚好指向最小的元素。 因此终止条件为:两个指针相邻。 (last-first)==1 

[4] 上述代码在数组中出现重复数字时,可能会报错  考虑数组{0,1,1,1,1}

其两个旋转数组:{1,0,1,1,1}    {1,1,1,0,1}   

取中间元素为1,由于首元素=中间元素=尾元素=1,无法判断中间元素时属于哪一个数组。这种情况下需要采用顺序查找(即方法1)。

//实现1
class Solution { public: int minNumberInRotateArray(vector<int> rotateArray) { int arrSize = rotateArray.size(); if(rotateArray.empty()) return 0; //已排序的递增数组{1,2,3,4,5} 不能有等号:如{1,0,1,1,1},会直接返回0 if(rotateArray[0]<rotateArray[arrSize-1]) return rotateArray[0]; //旋转数组--二分查找 int front = 0, back = arrSize - 1; int mindle = (front + back)/2; while((back-front)!=1){//终止条件:没有达到相邻的时候 //if(arrSize == 1) return rotateArray[0]; //只有一个元素时,不判断,会使用顺序搜索 //只有一个元素时,back=front=0,back-front!=1, 中间元素=front元素=back元素,进入顺序求解
if (rotateArray[front]==rotateArray[mindle] && rotateArray[front]==rotateArray[back])//放在内部 //无法判断中间元素归属哪一个数组,使用顺序查找 return searchMin(rotateArray,arrSize); //属于前面的数组
if(rotateArray[front]<=rotateArray[mindle]) front = mindle;
//属于后面的数组 else if (rotateArray[back]>=rotateArray[mindle]) back = mindle; mindle = (front + back)/2; } return rotateArray[back]; } int searchMin(vector<int> rotateArray,int arrSize){ //用判断为空否:不用,调用前,已经判断了 int resMin = rotateArray[0]; for(int i = 1;i<arrSize;i++) if(rotateArray[i]<resMin){ resMin = rotateArray[i]; break; } return resMin; } };
//实现2
class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        int size = rotateArray.size();
        if(size == 0){
            return 0;
        }
        int left = 0,right = size - 1;
        int mid = left;
        // rotateArray[left] >= rotateArray[right] 确保旋转
        //如果不满足条件,则说明该数组是排好序的,{1,2,3,4,5}.则返回第一个元素即可。因此mid初始为left
        while(rotateArray[left] >= rotateArray[right]){
            // 终止条件:两指针相邻
            if(right - left == 1){
                mid = right; //后一个指针即为最小值
                break;
            }
            mid = left + (right - left) / 2;
            // rotateArray[left] rotateArray[right] rotateArray[mid]三者相等
            // 无法确定中间元素是属于前面还是后面的递增子数组
            // 只能顺序查找
            if(rotateArray[left] == rotateArray[right] && rotateArray[left] == rotateArray[mid]){
                return MinOrder(rotateArray,left,right);
            }
            // 中间元素位于前面的递增子数组,此时最小元素位于中间元素的后面
            if(rotateArray[mid] >= rotateArray[left]){
                left = mid;
            }
            // 中间元素位于后面的递增子数组,此时最小元素位于中间元素的前面
            else{
                right = mid;
            }
        }
        return rotateArray[mid];
    }
private:
    // 顺序寻找最小值
    int MinOrder(vector<int> &num,int left,int right){
        int result = num[left];
        for(int i = left + 1;i < right;++i){
            if(num[i] < result){
                result = num[i];
                break;
            }
        }
        return result;
    }
};

2) 二分查找 O(logn)  没有考虑旋转数组特性  

任意数组都可以查找最小值 但是做题时,建议考虑题目中数组的特性进行编程

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        size_t len = rotateArray.size();
        if(len == 0)
            return 0;
        if(len == 1)
            return rotateArray[0];
        vector<int>::iterator low = rotateArray.begin();
        vector<int>::iterator mid;
        vector<int>::iterator high = rotateArray.end()-1;
        while(low <= high)
        {
            //防止迭代器失效
            mid = low + (high - low)/2;
            if(*mid >*high) //中间元素比最后一个元素大,输入前一个数组
            {   //mid所对元素>最后的元素,则最小元素一定不是mid,因此移动low指针到mid+1,缩小查找范围
                low = mid + 1; 
            }
            else if(*mid < *high)
            {   //不能确定mid是不是最小元素,因此不能减一:high = mid -1 (error)
                high = mid;
            }
            else
            {   // *mid = *high,high所对的值,在数组中已经存在,可不考虑该值,向前移一位
                high = high-1;
            }
            if(low == high) //终止条件:low与high指针指向同一个元素(地址)
            {
                break;
            }
        }
        //程序结束时,low与high指向相同的地址,返回哪一个都可以
        return *low;
        //return *high;
    }
};

  

 

posted @ 2019-02-21 11:15  GuoXinxin  阅读(132)  评论(0编辑  收藏  举报