算法题学习---旋转数组的最小数字

104 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第22天,点击查看活动详情

题目

描述

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

数据范围:1≤n≤10000,数组中任意元素的值:0≤val≤10000

要求:空间复杂度:O(1),时间复杂度:O(logn)

示例1

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

示例2

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

分析

二分法

参考之前进行二分查找的思路,我们将数组分为两个部分,然后对中点值进行比较,注意数组的前半部分和后半部分都是有序的,我们不知道的是中点落在哪个部分,接下来就要通过中点值与右边边界点的值对比来找出最小值在哪个部分。

  • 如果中点值大于右边界的值,说明最小值一定在右半部分;
  • 如果中点值小于右边界的值,说明最小值一定在左半部分;
  • 如果中点值与右边界的值相等,此时我们需要逐步缩小右边界,减一即可。

直到两个边界相遇,那么最小值就找到了。

分治法

划分:我们使用中点值将数组无限划分为两个子数组,直到其中只有一个元素,那么此时,子数组的最小值即为该元素值;

合并:一个数组的最小值总等于两个子数组中最小的那个值。

使用分治法也能够找到最终的答案。

代码示例

如上所说的两种方法:

二分法

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        int begin = 0;
        int end = rotateArray.size() - 1;
        while(begin < end)
        {
            int mid = (begin + end) / 2;
            if(rotateArray[mid] == rotateArray[end])
            {
                end -= 1;
            }
            else if(rotateArray[mid] > rotateArray[end])
            {
                begin = mid + 1;
            }
            else if(rotateArray[mid] < rotateArray[end])
            {
                end = mid;
            }
        }
        return rotateArray[begin];
    }
};

分治法

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        int begin = 0;
        int end = rotateArray.size() - 1;
        int minNum = 0;
        MergeMinNumber(rotateArray, begin, end, minNum);
        return minNum;
    }

    void MergeMinNumber(vector<int> &rotateArray, int begin, int end, int &minNum)
    {
        if(begin == end)
        {
            minNum = rotateArray[begin];
            return;
        }
        int mid = (begin + end) / 2;
        int min1 = 0;
        int min2 = 0;
        MergeMinNumber(rotateArray, begin, mid, min1);
        MergeMinNumber(rotateArray, mid + 1, end, min2);
        minNum = min1 < min2 ? min1 : min2;
    }
};