剑指 Offer 11. 旋转数组的最小数字

117 阅读3分钟

Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情

一、题目描述

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。

给你一个可能存在 重复 元素值的数组 numbers ,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一次旋转,该数组的最小值为 1。  

注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。

示例

示例1:

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

示例2:

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

提示

  • n == numbers.length
  • 1 <= n <= 5000
  • -5000 <= numbers[i] <= 5000
  • numbers 原来是一个升序排序的数组,并进行了 1 至 n 次旋转

难度:简单

此题为简单题,兄弟们可以重拳出击!

记得这题最开始做的时候不是简单题目,现在降级了,不知道是题目简单了还是人刷题的人厉害了。

二、思路分析

众所周知,力扣上,简单题是真简单,中等题有些困难,困难题看不懂答案。

题目解析

  1. 一个非降序排列的数组中所有元素进行一定程度的旋转之后,找出数组中最小的元素。对于非降序的原数组,第一个元素是最小的,但是经过一定程度的旋转后,最小元素不知道被移动到哪里,找起来还是要费点功夫。
  2. 简单的方法,可以通过对数组进行一次遍历,并使用全局变量存储当前最小元素,在遍历时比较和更新最小值,遍历完成后全局变量就是最小元素。
  3. 如果数组长度过大,或时间性能有要求,则需要使用二分法来寻找最小元素
    • 二分法最适宜的序列是有序序列,可以快速的判断并丢弃一半数据
    • 旋转后的数组虽然整体不是有序的,但是也满足一定的规律,可以通过修改二分法判断条件来满足
    • 对于旋转后非有序的数组,使用二分法获取中间位置元素时,有情况如下:
      • 中间位置元素大于右端点时,说明最小元素一定在右半段,此时左端点移动为 mid+1
      • 中间位置元素小于右端点时,说明最小元素在包含中间元素的左半段,此时右端点为 mid
      • 中间位置元素等于右端点时,不能判断最小元素在哪里,但是因为 mid 元素等于右端点,因此可以舍去右端点的值,此时最小值不会丢失,且 mid 可以继续进行二分
      • 循环至 l=r 时结束,当前即最小值
    • 如果数组旋转后元素位置没有变,则依然满足上述方法,此时一直是右端点左移

三、AC 代码

二分法查找最小数字

  • 二分法查找,考虑mid位置
public int minArray(int[] numbers) {
    int l = 0, r = numbers.length-1;
    while(l < r){
        int mid = l + (r-l)/2;
        if(numbers[mid] > numbers[r]){
            l = mid + 1;
        }else if(numbers[mid] < numbers[r]){
            r = mid;
        }else{
            r--;
        }
    }
    return numbers[r];
}

image.png

四、总结

知识点

  • 二分法的边界问题
  • 两个端点何时等于 mid,何时等于 mid+/-1,需要根据实际情况判断

最后

阳春三月,算法刷起来!LeetCode 剑指 Offer

简单题,不需要考虑太多,开干就是了。