旋转数组的最小数字[C++版]

本文介绍如何利用二分法解决找到旋转非递减排序数组的最小元素问题,详细阐述了解题思路和特殊情况处理,并给出了C++实现代码。

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

旋转数组的最小数字[C++版]

问题描述

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

解决思想

最简单的方法就是遍历法,但是对于这种已经局部排序完成的数组有些浪费了。
我们可以研究下其性质。以[3, 4, 5, 1, 2]这个数组为例,我们可以发现其由两个有序的子数组组成,即[3, 4, 5]和[1, 2]。最显著的特点是最小的元素1位于两个有序的子数组交界处。我们利用二分法来解决这个问题。
在这里插入图片描述
如上图所示,我们设定指针r和l分别指向数组的第一个元素和最后一个元素。既然我们使用二分法,我们就考虑设定中间指针m=(l + r) / 2。如上图所示,第一次设定m,我们可以得到Array[m] = 5,我们通过比较发现5 >= 3,而且我们假定前面的子数组中所有元素>=后面的子数组中的所有元素,好,我们就抛弃了5之前所有的元素,为啥抛弃呢?因为他们都不是最小的。好,我们令l = m,我们就到了图中的第三步。我们再次计算m = (l + r) / 2,得到了Array[m] = 1,我们发现1 <= 2,也就是1肯定位于后面的那个子数组中,那么1后面的我们都不看了,抛弃1后面的所有元素,我们令r = m。如此循环下去,直到我们发现l = r - 1,也就是这俩指针相邻了。此时得到了我们的最小值Array[r]。
每次搜索范围都减小一半,充分地利用了”旋转数组“的性质,时间复杂度O(logn)

我的代码

class Solution {
public:
	// 形参是vector容器,这里参考了牛客网考试的形式
    int minNumberInRotateArray(vector<int> a){
        int size = a.size();
        // 根据题目要求,若数组长度为0,返回0
        if(size == 0){
        	return 0;
        }
        // 声明并赋值l, r, m三个指针
        int l = 0;
        int r = size - 1;
        // m在这儿赋值仅仅为了初始化
        int m = 0;
        // 旋转数组的性质前面的子数组元素 >= 后面的子数组元素
        while(a[l] >= a[r]){
        	// 若两个指针相邻则代表找到了最小的元素,并将其值赋值给m以便return,跳出循环不再寻找
            if(r - l == 1){
                m = r;
                break;
            }
            // 根据l, r找m
            m = (l + r)/2;
            // 在前面的数组?
            if(a[m] >= a[l]){
                l = m;
             // 在后面的数组?
            }else if(a[m] <= a[r]){
                r = m;
            }
        }
        // 返回最小值
        return a[m];
    }
};

特殊情况

当数组的最后一个元素=数组的第一个元素=中间元素的时候,如[1, 0, 1, 1, 1],没办法了,只能遍历,也就是顺序查找了。

class Solution {
public:
    int minNumberInRotateArray(vector<int> a){
        int size = a.size();
        if(size == 0){return 0;}
        int l = 0;
        int r = size - 1;
        int m = 0;
        while(a[l] >= a[r]){
            if(r - l == 1){
                m = r;
                break;
            }
            m = (l + r)/2;
            // 特殊情况 顺序查找
            if(a[l] == a[r] && a[l] == a[m]){
                return findByOrder(a);
            }
            if(a[m] >= a[l]){
                l = m;
            }else if(a[m] <= a[r]){
                r = m;
            }
        }
        return a[m];
    }
private:
	// 特殊情况:遍历,即顺序查找
    int findByOrder(vector<int> a){
        int size = a.size();
        int min = a[0];
        for(int i = 0; i < size; i++){
            if(min >= a[i]){
                min = a[i];
            }
        }
        return min;
    }
};

总结

对于已经排好序的数组,我们要考虑尽量降低其查找的复杂度。二分法在这种“旋转数组”的表现力明显是由于暴力法的。
加油,为了未来!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值