一刷258-二分模块-154. 寻找旋转排序数组中的最小值 II(h)(同:剑指 Offer 11. 旋转数组的最小数字)

该博客介绍了如何在可能存在重复元素的旋转排序数组中寻找最小元素的算法。通过二分法,可以在对数时间内找到最小值。在最坏情况下,时间复杂度为O(N),空间复杂度为O(1)。算法流程包括初始化双指针,然后进行循环二分,根据中间值与右侧值的关系缩小搜索范围,直到找到最小值。
摘要由CSDN通过智能技术生成
题目:
已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。
例如,原数组 nums = [0,1,4,4,5,6,7] 在变化后可能得到:
若旋转 4 次,则可以得到 [4,5,6,7,0,1,4]
若旋转 7 次,则可以得到 [0,1,4,4,5,6,7]
注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为
数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。

给你一个可能存在 重复 元素值的数组 nums ,它原来是一个升序排列的数组,
并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。

你必须尽可能减少整个过程的操作步骤。
-----------------------------
示例 1:
输入:nums = [1,3,5]
输出:1

输入:nums = [2,2,2,0,1]
输出:0
 
提示:
n == nums.length
1 <= n <= 5000
-5000 <= nums[i] <= 5000
nums 原来是一个升序排序的数组,并进行了 1 至 n 次旋转
 
进阶:这道题与 寻找旋转排序数组中的最小值 类似,但 nums 可能包含重复元素。
允许重复会影响算法的时间复杂度吗?会如何影响,为什么?
---------------------
思路:
寻找旋转数组的最小元素即为寻找 右排序数组 的首个元素 nums[x] ,称 x 为 旋转点 。

在这里插入图片描述

排序数组的查找问题首先考虑使用 二分法 解决,
其可将 遍历法 的 线性级别 时间复杂度降低至 对数级别 。
--------------
算法流程:
初始化: 声明 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[i]作比较?
二分目的是判断 m 在哪个排序数组中,从而缩小区间。而在 nums[m] > nums[i]情况下,无法判断 m 在哪个排序数组中。本质上是由于 j 初始值肯定在右排序数组中; i 初始值无法确定在哪个排序数组中。
-------
复杂度分析:
时间复杂度 O(log2N) : 在特例情况下(例如 [1, 1, 1, 1]),会退化到 O(N)。
空间复杂度 O(1) : i , j , m 变量使用常数大小的额外空间。
class Solution {
    public int minArray(int[] numbers) {
        int left = 0, right = numbers.length - 1;
        while (left < right) {
            int mid = left + ((right - left) >> 1);
            if (numbers[mid] > numbers[right]) {// mid在左排序数组 
                left = mid + 1;
            }else if (numbers[mid] < numbers[right]){//mid在有右排序数组
                right = mid;
            }else right--;//numbers[mid] == numbers[right] 无法判断mid在哪个排序数组:缩小范围
        }
        return numbers[right];
    }
}

LC

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值