leetcode 剑指offer 11 旋转数组的最小数字

问题描述:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2][1,2,3,4,5] 的一个旋转,该数组的最小值为1。

示例 1:

输入:[3,4,5,1,2]
输出:1

示例 2:

输入:[2,2,2,0,1]
输出:0

解题思路:

如下图所示,寻找旋转数组的最小元素即为寻找 右排序数组 的首个元素 nums[x],称x旋转点截屏2020-12-28 上午8.28.38

PS:

  • 主要是要利用旋转数组的性质 左排序数组任意元素 >= 右排序数组任意元素
  • 由于是排序数组,二分法解决问题,可将 遍历法线性级别 时间复杂度降低至 对数级别

所以采用以下算法步骤:

  • 初始化: 声明i, j双指针分别指向nums数组左右两端;
  • 循环二分:m=(i+j)/2为每次二分的中点( "/" 代表向下取整除法,因此恒有i≤m<j),可分为以下三种情况:
    • nums[m]>nums[j]时:m一定在左排序数组中,即旋转点x一定在[m+1,j]闭区间内,因此执行i=m+1
    • nums[m]<nums[j]时:m一定在右排序数组中,即旋转点x一定在[i,m]闭区间内,因此执行j=m
    • nums[m]=nums[j]时:无法判断m在哪个排序数组中,即无法判断旋转点x[i,m] 还是 [m+1,j] 区间中。解决方案: 执行 j=j-1缩小判断范围,分析见下文。
  • 返回值:i=j时跳出二分循环,并返回 旋转点的值nums[i]即可。

实际上,当出现nums[m] = nums[j]时,一定有区间 [i,m] 内所有元素相等 或 区间[m,j]内所有元素相等(或两者皆满足)。对于寻找此类数组的最小值问题,可直接放弃二分查找,而使用线性查找替代。

解法:

class Solution {
public:
    int minArray(vector<int>& numbers) {
        int i = 0, j = numbers.size() - 1;
        while(i < j)
        {
            int m = (i + j)/2;
            if(numbers[j] < numbers[m])
            {
                i = m + 1;
            }
            else if(numbers[m] < numbers[j])
            {
                j = m;
            }
            else
            {
                for (auto i = numbers.cbegin();(i + 1)!= numbers.cend();)
                {
                    if (*i > *(++i))
                    {
                        return *i;
                    }
                }
                return *(numbers.cbegin());
            }
        }
        return numbers[i];
    }
};
posted @ 2020-12-28 08:44  zeroluo  阅读(58)  评论(0)    收藏  举报