• 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;
        }
    };
    

      

  • 相关阅读:
    shell安装kettle
    shell安装jdk
    shell脚本之系统初始化设置(设置selinux、设置ulimit、设置firewalld、设置time_zone)
    关闭防火墙
    todo的使用
    Android Dagger2.0 学习一下
    Android零碎知识点
    windows7 服务中上找不到mysql
    AS 3.1 多library合并打包成aar的正确方式(fat-aar)
    Alexander的Python机器学习 之目录分析。
  • 原文地址:https://www.cnblogs.com/GuoXinxin/p/10400566.html
Copyright © 2020-2023  润新知