Skip to content

M153.寻找旋转排序数组中的最小值

binary search, https://leetcode.cn/problems/find-minimum-in-rotated-sorted-array/

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

  • 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
  • 若旋转 7 次,则可以得到 [0,1,2,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 ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

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

示例 2:

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

示例 3:

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

提示:

  • n == nums.length
  • 1 <= n <= 5000
  • -5000 <= nums[i] <= 5000
  • nums 中的所有整数 互不相同
  • nums 原来是一个升序排序的数组,并进行了 1n 次旋转

这道题要求在 O(logn) 的时间复杂度内找到旋转排序数组中的最小值。由于数组在旋转前是升序的,且旋转后依然保持局部有序,我们可以使用 二分查找(Binary Search) 来解决。

核心思路

在旋转排序数组中,数组被分成了两个升序的部分:

  1. 左侧部分的所有元素都大于右侧部分的所有元素(如果发生了旋转)。
  2. 我们要寻找的最小值,正是右侧部分的第一个元素。

我们可以通过比较中间元素 nums[mid]右边界元素 nums[right] 来确定最小值所在的区间:

  1. 如果 nums[mid] < nums[right]

    • 说明从 midright 这一段是升序的。
    • 最小值可能是 nums[mid] 本身,也可能在 mid 的左侧。
    • 因此,我们将右边界收缩:right = mid
  2. 如果 nums[mid] > nums[right]

    • 说明从 midright 之间发生了“断层”(旋转点在其中)。
    • 最小值一定在 mid 的右侧。
    • 因此,我们将左边界收缩:left = mid + 1
  3. 循环结束条件

    • left == right 时,我们就找到了数组的最小值。

代码实现

python
from typing import List

class Solution:
    def findMin(self, nums: List[int]) -> int:
        left, right = 0, len(nums) - 1
        
        # 二分查找
        while left < right:
            mid = left + (right - left) // 2
            
            # 如果中间值小于右边界值,说明右半部分是递增的
            # 最小值可能是 mid,或者在 mid 的左边
            if nums[mid] < nums[right]:
                right = mid
            # 如果中间值大于右边界值,说明最小值一定在 mid 的右边
            else:
                left = mid + 1
        
        # 最终 left == right,指向的就是最小值
        return nums[left]

复杂度分析

  • 时间复杂度O(logn)。每次循环都将搜索区间缩小一半。
  • 空间复杂度O(1)。只使用了常数级别的额外空间。

为什么不和 nums[left] 比较?

如果数组没有旋转(即它是完全升序的,如 [1, 2, 3]),nums[mid] 会大于 nums[left]。但在旋转数组(如 [4, 5, 1, 2, 3])中,nums[mid] 同样可能大于 nums[left]。 这会导致逻辑判断变得复杂。而比较 nums[mid]nums[right] 可以非常清晰地判断 mid 是落在“大的左半段”还是“小的右半段”,逻辑更加统一。