• 每日一题


    题目信息

    • 时间: 2019-07-23

    • 题目链接:Leetcode

    • tag: 二分查找

    • 难易程度:简单

    • 题目描述:

      把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [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
    

    解题思路

    本题难点

    排序数组的旋转,找到旋转点,性能最佳。

    具体思路

    寻找旋转数组的最小元素即为寻找 右排序数组 的首个元素 numbers[x] ,称 x为 旋转点

    排序数组的查找问题首先考虑使用 二分法 解决,其可将遍历法的 线性级别 时间复杂度降低至 对数级别

    • 循环二分:
      1. 当 numbers[m] > numbers[j]时: m 一定在 左排序数组 中,即旋转点 x 一定在 [m+1,j] 闭区间内,因此执行 i=m+1;
      2. 当 numbers[m] < numbers[j] 时:m 一定在 右排序数组 中,即旋转点 x 一定在[i,m] 闭区间内,因此执行 j=m;
      3. 当 numbers[m] == numbers[j] 时: 无法判断 m 在哪个排序数组中,即无法判断旋转点 x 在 [i,m] 还是 [m+1,j] 区间中。解决方案: 执行 j=j−1 缩小判断范围 。

    展开分析 numbers[m] == numbers[j] 情况

    • 无法判定 m 在左(右)排序数组: 设以下两个旋转点值为 0 的示例数组,则当 i=0, j=4 时 m=2 ,两示例结果不同。

      • 例 [1,0,1,1,1] :旋转点 x=1 ,因此 m=2 在 右排序数组 中。

      • 例 [1,1,1,0,1] :旋转点 x=3 ,因此 m=2 在 左排序数组 中。

    • j=j−1 操作的正确性证明:只需证明每次执行此操作后,旋转点 x 仍在 [i,j] 区间内即可。

      • 若 m 在右排序数组中: numbers[m] == numbers[j] ,因此数组 [m,j](恒有 m<j)区间内所有元素值相等,执行 j=j−1 只会抛弃一个重复值,因此旋转点 x 仍在 [i,j] 区间内。

      • 若 m 在左排序数组中: 由于 左排序数组 任一元素 >= 右排序数组 任一元素 ,因此可推出旋转点元素值 numbers[x] <= numbers[j] == numbers[m],则有:

        1. 若 numbers[x] < numbers[j] : 即 j 左方仍有值更小的元素,执行 j=j−1 后旋转点 x 仍在 [i,j] 区间内。

        2. numbers[x] == numbers[j] 分为以下两种情况。

          当 j>xj>x : 易得执行 j=j−1j=j−1 后旋转点 xx 仍在 [i,j] 区间内。

          当 j=x: 特殊情况,即执行 j=j−1 后旋转点 x 可能不在 [i,j] 区间内。例如 [1,1,1,2,3,1] ,当 i=0 , m=2 , j=5 时执行 j=j−1 后虽然 丢失了旋转点索引 x=5 ,但最终返回值仍正确(最终返回的 numbers[0] 等于旋转点值 numbers[5] ),这是因为:之后的二分循环一直在执行 j=m ,而区间 [i,m] 内的元素值一定都等于旋转点值 numbers[x] ( ∵ 区间内元素值既要满足 ≥ 也要满足 ≤ numbers[x]) ,因此 仍可保证正确的返回值 。

    提示 是否可以用 numbers[m]numbers[i] 比较做代替?不可以。因为做比较的目的是判断 m 在哪个排序数组中。但在 numbers[m] > numbers[i]情况下,无法判断 m 在哪个排序数组中。本质是因为 j 初始值肯定在右排序数组中; i 初始值无法确定在哪个排序数组中。

    代码

    class Solution {
        public int minArray(int[] numbers) {
            int l = 0;
            int r = numbers.length-1;
            while(l < r){
                int mid = (l + r)/2;
                if(numbers[mid] > numbers[r]){
                    l = mid + 1;
                }else if(numbers[mid] < numbers[r]){
                    r = mid;
                }else{
                    r--;
                }
            }
            return numbers[l];
        }
    }
    

    复杂度分析:

    • 时间复杂度 O(logN) : 在特例情况下(例如 [1,1,1,1]),会退化到 O(N)。
    • 空间复杂度 O(1) : i , j , m指针使用常数大小的额外空间。

  • 相关阅读:
    4.22日日常记录
    ajax再接触
    一些小姿势
    打星功能
    悟透javascript读书笔记
    初接触eclipse和前后端调试问题 待续
    python实现后台员工管理系统
    python实现增删改查操作
    python实现6种方法打印九九乘法表
    python实现猜数字游戏
  • 原文地址:https://www.cnblogs.com/ID-Wangqiang/p/13368232.html
Copyright © 2020-2023  润新知