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

题目

标题和出处

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

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

难度

5 级

题目描述

要求

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

  • 若旋转 4 \texttt{4} 4 次,则可以得到 [4,5,6,7,0,1,2] \texttt{[4,5,6,7,0,1,2]} [4,5,6,7,0,1,2]
  • 若旋转 7 \texttt{7} 7 次,则可以得到 [0,1,2,4,5,6,7] \texttt{[0,1,2,4,5,6,7]} [0,1,2,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,返回数组中的最小元素。

要求时间复杂度是 O(log   n) \texttt{O(log n)} O(log n)

示例

示例 1:

输入: nums   =   [3,4,5,1,2] \texttt{nums = [3,4,5,1,2]} nums = [3,4,5,1,2]
输出: 1 \texttt{1} 1
解释:原数组为 [1,2,3,4,5] \texttt{[1,2,3,4,5]} [1,2,3,4,5],旋转 3 \texttt{3} 3 次得到输入数组。

示例 2:

输入: nums   =   [4,5,6,7,0,1,2] \texttt{nums = [4,5,6,7,0,1,2]} nums = [4,5,6,7,0,1,2]
输出: 0 \texttt{0} 0
解释:原数组为 [0,1,2,4,5,6,7] \texttt{[0,1,2,4,5,6,7]} [0,1,2,4,5,6,7],旋转 4 \texttt{4} 4 次得到输入数组。

示例 3:

输入: nums   =   [11,13,15,17] \texttt{nums = [11,13,15,17]} nums = [11,13,15,17]
输出: 11 \texttt{11} 11
解释:原数组为 [11,13,15,17] \texttt{[11,13,15,17]} [11,13,15,17],旋转 4 \texttt{4} 4 次得到输入数组。

数据范围

  • 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 中的所有整数各不相同
  • nums \texttt{nums} nums 是一个升序排序的数组进行了 1 \texttt{1} 1 n \texttt{n} n 次旋转后的数组

解法

思路和算法

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

对于长度为 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] 可能是一个有序子数组或者两个有序子数组。

如果 [ 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 \textit{low} low 处的整数就是最小值。

如果 [ 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] 中寻找最小值所在下标。取 mid \textit{mid} mid low \textit{low} low high \textit{high} high 的平均数向下取整,得到下标 mid \textit{mid} mid 处的数,判断该数位于哪一个有序子数组中,调整查找的下标范围。

  • 如果 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}] > \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}] > \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[mid] < nums[low]) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }
        return nums[low];
    }
}

复杂度分析

  • 时间复杂度: O ( log ⁡ n ) O(\log n) O(logn),其中 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)

  • 空间复杂度: 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、付费专栏及课程。

余额充值