剑指offer之旋转数组的最小数字C++解法

探讨了在非减排序的旋转数组中寻找最小元素的高效算法,利用二分查找法实现O(logn)的时间复杂度,特别关注了数组旋转、二分查找和边界条件处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

剑指offer之旋转数组的最小数字C++解法


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

首先,先排序再O(1)查找或者从头到尾遍历查找的办法肯定不是这道题要考察的关键。
在有序数组中,利用二分查找法可以实现O(logn)的查找。本题给出的数组在一定程度上是有序的,因此可以尝试使用二分法来查找最小元素。
二分法查找的原理: 分别定义三个指针low、high、mid,分别指向待查元素所在范围的下界和上界及区间的中间位置,即mid=(low+high)/2,让关键字与mid所指的数比较,若相等则查找成功并返回mid,若关键字小于mid所指的数则high=mid-1,否则low=mid+1,然后继续循环直到找到或找不到为止。
根据本题的情况,分别定义三个指针left,right,mid,分别指向待查元素的下界、上界和区间的中间位置,即mid=(left+high)/2,若left所指的数小于mid所指的数,说明mid位于前面的递增子数组,则left=mid,移动后left仍然位于前面的递增子数组;若right所指的数大于mid所指的数,说明mid位于后面的递增子数组,则right=mid,移动后right仍然位于后面的递增子数组;当right和left相遇时,right-left==1,即left指向前面递增子数组的最后一个数,right指向后面递增子数组的第一个数,这时候就找到了最小数,即right所指的数。但这种方法只解决了最常见的一种情况(即数组中不存在重复元素且搬到后面的元素个数大于0),还有几种特殊情况:

  1. 把前面0个元素元素搬到后面,这仍然是数组的一个旋转
  2. 数组中存在重复元素,如{1,0,1,1,1,},{1,1,1,0,1},这种情况下left、right、mid三个指针指向的数相等,无法判断mid所指是属于哪个子数组

对于第一个情况,即left所指小于right所指,这时旋转数组的第一个数即为目标的最小数,可以添加一个判断:当left所指小于right所指时,直接返回left所指数即可;
对于第二个情况,无法直接通过上面的二分法进行查找,可以考虑对这种情况下的数组进行整体遍历或者排序来进行查找,或者直接通过向右移动left指针来缩小二分查找的范围再进行查找,缩小范围之后同样有可能出现第一种情况,如果没有出现第一种情况,则按照普通情况进行处理,也将得道最终结果。
综上所述, 循环查找将存在两个出口,一个是按照普通情况循环N次后left和right相遇,跳出循环;另一种则是在特殊情况下遇到left指向的数小于right指向的数而跳出循环返回left指向的数。
所以,代码如下:

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        if (rotateArray.empty()) return 0;
        int left = 0, right = rotateArray.size() - 1;
        int mid = left;
        while (rotateArray[left]>=rotateArray[right]) {     //出口1       
            if(right-left==1){
                mid = right;
                break; //出口2
            }
            mid = (left+right) / 2; //这条语句要在出口2 后面,否则结果错误
            if (rotateArray[left] < rotateArray[mid])
                left = mid;
            else if (rotateArray[mid] < rotateArray[right])
                right = mid;
            else {
                ++left;  //缩小查找范围
            }
        }
        return rotateArray[mid];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值