二分查找题目:寻找旋转排序数组中的最小值 II

题目

标题和出处

标题:寻找旋转排序数组中的最小值 II

出处:154. 寻找旋转排序数组中的最小值 II

难度

6 级

题目描述

要求

已知一个长度为 n \texttt{n} n 的数组预先按照升序排序,经过 1 \texttt{1} 1 n \texttt{n} n旋转。例如,原数组 nums   =   [0,1,4,4,5,6,7] \texttt{nums = [0,1,4,4,5,6,7]} nums = [0,1,4,4,5,6,7] 在变化后可能得到:

  • 若旋转 4 \texttt{4} 4 次,则可以得到 [4,5,6,7,0,1,4] \texttt{[4,5,6,7,0,1,4]} [4,5,6,7,0,1,4]
  • 若旋转 7 \texttt{7} 7 次,则可以得到 [0,1,4,4,5,6,7] \texttt{[0,1,4,4,5,6,7]} [0,1,4,4,5,6,7]

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

给定一个可能存在重复元素的旋转后的排序数组 nums \texttt{nums} nums,返回数组中的最小元素。

要求尽可能减少总操作次数。

示例

示例 1:

输入: nums   =   [1,3,5] \texttt{nums = [1,3,5]} nums = [1,3,5]
输出: 1 \texttt{1} 1

示例 2:

输入: nums   =   [2,2,2,0,1] \texttt{nums = [2,2,2,0,1]} nums = [2,2,2,0,1]
输出: 0 \texttt{0} 0

数据范围

  • n = nums.length \texttt{n} = \texttt{nums.length} n=nums.length
  • 1 ≤ n ≤ 5000 \texttt{1} \le \texttt{n} \le \texttt{5000} 1n5000
  • -5000 ≤ nums[i] ≤ 5000 \texttt{-5000} \le \texttt{nums[i]} \le \texttt{5000} -5000nums[i]5000
  • nums \texttt{nums} nums 是一个升序排序的数组进行了 1 \texttt{1} 1 n \texttt{n} n 次旋转后的数组

进阶

这道题和「寻找旋转排序数组中的最小值」相似,但是 nums \texttt{nums} nums 可能包含重复元素。这会影响到时间复杂度吗?会如何影响,为什么?

解法

思路和算法

这道题是「寻找旋转排序数组中的最小值」的进阶,给定的数组 nums \textit{nums} nums 中可能包含重复元素。这道题也可以使用二分查找的思想,但是由于数组中可能存在重复元素,因此无法保证达到二分查找的时间复杂度。

为了寻找旋转排序数组中的最小值,需要得到旋转排序数组中的最小值所在下标。

对于长度为 n n n 的升序数组,如果旋转次数等于 n n n 则与旋转前的数组相同,此时最小值位于下标 0 0 0。如果旋转次数小于 n n n 则旋转 i i i 次之后最小值位于下标 i i i。当最小值位于下标 i i i 0 < i < n − 1 0 < i < n - 1 0<i<n1 时,下标 i i i 左侧的任意整数一定大于等于下标 i i i 右侧的任意整数。

low \textit{low} low high \textit{high} high 分别表示二分查找的下标范围的下界和上界,初始时 low \textit{low} low high \textit{high} high 分别为数组的最小下标和最大下标。每次查找时, [ low , high ] [\textit{low}, \textit{high}] [low,high] 可能是一个有序子数组或者两个有序子数组。

如果 nums [ low ] < nums [ high ] \textit{nums}[\textit{low}] < \textit{nums}[\textit{high}] nums[low]<nums[high],则 [ low , high ] [\textit{low}, \textit{high}] [low,high] 一定是一个有序子数组,下标 low \textit{low} low 处的整数就是最小值。

如果 nums [ low ] ≥ nums [ high ] \textit{nums}[\textit{low}] \ge \textit{nums}[\textit{high}] nums[low]nums[high],则 [ low , high ] [\textit{low}, \textit{high}] [low,high] 可能是一个有序子数组或者两个有序子数组,其中只有当 nums [ low ] = nums [ high ] \textit{nums}[\textit{low}] = \textit{nums}[\textit{high}] nums[low]=nums[high] [ low , high ] [\textit{low}, \textit{high}] [low,high] 才可能是一个有序子数组,此时需要在范围 [ low , high ] [\textit{low}, \textit{high}] [low,high] 中寻找最小值所在下标。取 mid \textit{mid} mid low \textit{low} low high \textit{high} high 的平均数向下取整,得到下标 mid \textit{mid} mid 处的数,判断该数位于哪一个有序子数组中,调整查找的下标范围。

  • 如果 nums [ low ] = nums [ mid ] = nums [ high ] \textit{nums}[\textit{low}] = \textit{nums}[\textit{mid}] = \textit{nums}[\textit{high}] nums[low]=nums[mid]=nums[high],则无法确定两个有序子数组的分界位置(即第二个有序子数组的开始下标)是在 mid \textit{mid} mid 处、 mid \textit{mid} mid 的左边还是 mid \textit{mid} mid 的右边,此时无法将查找的下标范围缩小一半,因此在下标范围 [ low + 1 , high − 1 ] [\textit{low} + 1, \textit{high} - 1] [low+1,high1] 中继续查找。

  • 如果 nums [ mid ] < nums [ low ] \textit{nums}[\textit{mid}] < \textit{nums}[\textit{low}] nums[mid]<nums[low],则下标 mid \textit{mid} mid 位于以下标 high \textit{high} high 结束的有序子数组中,最小值位于下标 mid \textit{mid} mid 或其左边,因此在下标范围 [ low , mid ] [\textit{low}, \textit{mid}] [low,mid] 中继续查找。

  • 如果 nums [ mid ] ≥ nums [ low ] \textit{nums}[\textit{mid}] \ge \textit{nums}[\textit{low}] nums[mid]nums[low],则下标 mid \textit{mid} mid 位于以下标 low \textit{low} low 开始的有序子数组中,最小值位于下标 mid \textit{mid} mid 右边,因此在下标范围 [ mid + 1 , high ] [\textit{mid} + 1, \textit{high}] [mid+1,high] 中继续查找。

二分查找的条件是 low < high \textit{low} < \textit{high} low<high nums [ low ] ≥ nums [ high ] \textit{nums}[\textit{low}] \ge \textit{nums}[\textit{high}] nums[low]nums[high],当该条件不成立时,二分查找结束,此时的 low \textit{low} low 即为最小值所在下标。理由如下。

  • 如果 low < high \textit{low} < \textit{high} low<high 不成立,则 low = high \textit{low} = \textit{high} low=high,此时二分查找的范围仅限于一个下标。由于被排除的下标都不可能是最小值所在下标,因此 low \textit{low} low 为最小值所在下标。

  • 如果 nums [ low ] ≥ nums [ high ] \textit{nums}[\textit{low}] \ge \textit{nums}[\textit{high}] nums[low]nums[high] 不成立,则 nums [ low ] < nums [ high ] \textit{nums}[\textit{low}] < \textit{nums}[\textit{high}] nums[low]<nums[high],范围 [ low , high ] [\textit{low}, \textit{high}] [low,high] 中的最小元素位于下标 low \textit{low} low。由于被排除的下标都不可能是最小值所在下标,因此 low \textit{low} low 为最小值所在下标。

代码

class Solution {
    public int findMin(int[] nums) {
        int low = 0, high = nums.length - 1;
        while (low < high && nums[low] >= nums[high]) {
            int mid = low + (high - low) / 2;
            if (nums[low] == nums[mid] && nums[high] == nums[mid]) {
                low++;
                high--;
            } else if (nums[mid] < nums[low]) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }
        return nums[low];
    }
}

复杂度分析

  • 时间复杂度:平均情况是 O ( log ⁡ n ) O(\log n) O(logn),最差情况是 O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度。平均情况下,二分查找的次数是 O ( log ⁡ n ) O(\log n) O(logn),每次查找的时间是 O ( 1 ) O(1) O(1),时间复杂度是 O ( log ⁡ n ) O(\log n) O(logn)。最差情况下,数组 nums \textit{nums} nums 中的所有元素都相等,此时需要遍历整个数组。

  • 空间复杂度: O ( 1 ) O(1) O(1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的车尔尼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值