NC71.旋转数组的最小数字

一、题目描述

有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。

二、示例

示例一:
输入: [3,4,5,1,2]
返回值: 1

示例二:
输入: [3,100,200,3]
返回值: 3

三、主要思路

1.暴力解法

这道题也可以用暴力解法来做,无论数组怎么旋转,本质还是让我们找最小值,我们可以遍历整个数组找到最小值,暴力解法非常简单,但时间复杂度很高。

2.前后指针法

其实这个方法也不是什么高大上的方法,只是在暴力解法的基础上做了一些小小的优化。

我们可以定义两个指针cur和prev,cur指针在prev指针前面,每次循环,两个指针都向前走一步,直到cur的值比prev的值小为止,此时cur的值就是最小值。

在这里插入图片描述

但其实这种方法本质上也是遍历整个数组查找最小值,比上面的暴力解法好的地方只是有可能早一点退出循环,当遇到最坏情况的时候,时间复杂度和暴力解法是一样的。

3.二分比较法

查找的本质就是排除。我们上面介绍的方法都是一个一个在排除,效率非常低。我们可以借助二分查找的思想,一次排除一半的数据。

首先我们观察这道题给的数据,它们原先是非递减的有序的数据,经过旋转之后变成无序的数据,但无论它怎么旋转,最后的数据都可以分成前后两部分,并且这两部分内部都是非递减有序的。

在这里插入图片描述
并且我们还可以发现,旋转以后的数据被分成两组,前面一组的数据都是大于后面一组数据的。所以无论怎么旋转,最小值一定在后面一组数据中,并且是后面一组数据的第一个。换句话说,最小值在前面一组数据里最后一个数据的下一个位置。

所以我们可以定义left指针指向旋转后数组的第一个元素,定义right指针指向旋转后数组的最后一个元素。计算中间位置的坐标为mid,让mid的值与left的值进行比较:

  1. 如果mid的值比left的值大或者相等,说明mid的值属于前面一组数据,最小值在mid的右侧,此时我们就可以让left指向mid所在的位置,right的指向位置不变,继续计算新的mid值。
  2. 如果mid的值比left的值小,说明mid的值属于后面一组数据,最小值在mid的左侧,此时我们就可以让right指向mid所在的位置,lefi指向的位置不变,继续计算新的mid值。

直到left和right相邻时,right指向的值就是数组的最小值。

但是这道题有一个细节需要注意,由于题目说的是非递减数组,也就是说有可能会存在多个相同的值。有一个极端条件就是:如果left、right和mid指向的值都相等,我们是没办法判断mid的值是属于前面一组的数据还是属于后面一组的数据,此时我们只能针对这种情况从left到right进行遍历查找最小值。

class Solution {
  public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        int left = 0;
        int right = rotateArray.size() - 1;
        int mid = 0;

        while (rotateArray[left] >= rotateArray[right]) {
            // 停止条件
            if (right - left == 1) {
                mid = right;
                break;
            }
            mid = (left + right) / 2;

            // 处理left、right、mid三者相等的特殊情况
            if (rotateArray[left] == rotateArray[right] &&
                    rotateArray[mid] == rotateArray[left]) 
            {
                int minRes = rotateArray[left];
                for(int i = left + 1; i < right; i++)
                {
                    if(rotateArray[i] < minRes)
                    {
                        minRes = rotateArray[i];
                    }
                }
                return minRes;
            }

            // 正常情况
            if(rotateArray[mid] >= rotateArray[left])
            {
                left = mid;
            }
            else
            {
                right = mid;
            }
        }

        return rotateArray[mid];

    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值