《剑指offer》-[第2章:面试需要的基础知识 - 2.4:算法与数据操作 - 2.4.1:查找与排序] 题8:旋转数组的最小值

1、问题描述

将一个排序数组的开头若干个元素移动到数组的末尾,得到的数组称之为旋转数组。求旋转数组中的最小值。如{3,4,5,1,2}就是{1,2,3,4,5}的一个旋转数组,该旋转数组的最小值为1。数组的旋转其实就是就是数组的循环移位。

2、解题思路

  • 思路1:旋转数组是将排序数组中的开头若干个元素移动到数组的的末尾,所以旋转数组可以划分为两个递增的子数组,这两个子数组以数组的最小值作为分界线,并且前面递增的子数组中的第一个元素都要大于或等于后面递增子数组的最后一个元素(也有一些特殊情况)。
  • 当旋转数组的前面递增子数组的第一个元素大于或等于后面递增数组的最后一个元素时,我们可以采用二分查找来找到数组的最小值。
  • 首先准备两个指针p1,p2,p1初始化为指向前面递增子数组的第一个元素,p2初始化为指向后面递增数组的最后一个元素,取p1,p2中间位置的数middle,如果middle大于p1所指向的数,表示middle处于前面递增的子数组中,要查找的最小值位于middle的后面,所以p1指向middle;如果middle小于p1所指向的数,那么说明middle处于后面的递增子数组中,要查找的最小值位于middle的前面,所以令p2指向middle。
  • 无论是移动第一个指针还是第二个指针,查找范围都会缩小为原来的一半。按照上面的思路,第一个指针总是指向第一个递增数组的元素,而第二个指针总是指向第二个递增数组的元素,最终第一个指针会指向第一个递增数组的最后一个元素,第二个指针会指向第二个递增数组的第一个元素。
  • 但是,有两种情况需要特殊处理:(1)前面递增数组的第一个元素小于后面递增数组的最后一个元素。(2)p1、p2指向的元素和middle相等。
  • (1)这种情况是因为旋转数组由排序数组将开头的0个元素移动到末尾造成的,即旋转数组等于排序数组,此时直接进行二分查找即可。
  • (2)第二种情况是由于数组中有相同的数字造成的。如果p1、p2指向的数和middle相等,就无法判断middle是处于那个递增子数组中,比如10111和11101,这个时候只能顺序查找。
  • 归纳:当要查找的数具体值不知道的时候,我们可以尝试根据某些性质来判断查找的数和middle之间的位置关系。比如这道题,我们不知到旋转数组的最小值是多少,但是通过将middle和旋转数组的第一个元素比较,我们就能知道最小值是在middle的前面还是后面。

3、代码实现

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        if(rotateArray.size() == 0){
            return 0;
        }
        int len = rotateArray.size();
        if(rotateArray[0] < rotateArray[len-1]){
            return rotateArray[0];
        }
        int left = 0;
        int right = len - 1;
        int minindex = -1;
        //当满足第一个递增子数组的首元素小于等于第二个递增子数组的尾元素是,进行二分查找
        while(rotateArray[left] >= rotateArray[right]){
            //特别注意,通过不断折半,旋转子数组只包含两个元素的时候,循环终止,最小值为
            if(right - left == 1){
                minindex = right;
                break;
            }
            int middle = left + (right - middle)/2;
            //当中间位置的值等于第一个递增子数组的首元素和等于第二个递增子数组的尾元素
            //不能确定中间位置的元素落在哪个子数组,只能进行顺序查找
            if(rotateArray[middle] == rotateArray[left] && rotateArray[middle] == rotateArray[right]){
                return orderSearch(rotateArray);
            }
            if(rotateArray[middle] >= rotateArray[left]){
                left = middle;
            }
            else if(rotateArray[middle] <= rotateArray[right])
            {
                right = middle;
            }
        }
        return rotateArray[minindex];
    }
    int orderSearch(vector<int> rotateArray){
        int minval = rotateArray[0];
        for(int i = 1; i < rotateArray.size();++i){
            minval = min(minval, rotateArray[i]);
        }
        return minval;
        
    }
    
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Albert_YuHan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值