返回

旋转排序数组中的最小值Ⅱ

闲谈

旋转排序数组中的最小值Ⅱ

旋转排序数组中的最小值Ⅱ这道题是旋转排序数组中的最小值(153)的延伸题目。

在讲解本题之前,首先要对昨天的题目进行一个答疑。昨天有人问我为什么题目中讲的是与left进行比较,但是最后代码中写的时候变成了和right比较。这个确实是我讲的时候讲忘了,但是这其实是一个思维转化的问题:因为在旋转之前的数组中,左边的元素肯定小于右边的元素,所以与left进行比较;但是在旋转之后的数组中,左边的元素不一定是小于右边的元素,所以需要与right进行比较。

言归正传,我们来看本题。

本题与昨天那道题最大的区别在于,题目中说数组中可能包含重复的元素。这也就意味着,我们不能再用昨天那道题的解法了。因为昨天那道题的解法是基于这样一个事实:数组中不包含重复的元素。

那么,本题该怎么解呢?

我们还是从一个例子开始说起。假设我们有一个旋转排序数组nums = [4, 5, 6, 7, 0, 1, 2]。这个数组是按照递增顺序排序的,但是它被旋转了3次。

我们可以看到,这个数组中的最小值是0。但是,如果我们用昨天那道题的解法来解这道题,我们会得到最小值是4。这是因为昨天那道题的解法是基于这样一个事实:数组中不包含重复的元素。但是,在本题中,数组中包含重复的元素。

那么,我们该怎么解决这个问题呢?

我们可以使用一个二分查找的变种来解决这个问题。这个变种叫做“旋转二分查找”。

旋转二分查找的算法步骤如下:

  1. 初始化left和right分别为0和n-1。
  2. 计算中间位置mid。
  3. 如果nums[mid] < nums[right],那么最小值一定在[left, mid]之间。将right更新为mid-1。
  4. 如果nums[mid] > nums[right],那么最小值一定在[mid+1, right]之间。将left更新为mid+1。
  5. 如果nums[mid] == nums[right],那么我们需要继续查找最小值。将left更新为left+1,将right更新为right-1。
  6. 重复步骤2-5,直到left和right相等。
  7. 返回nums[left]。

这个算法的时间复杂度是O(logn)。

下面我们来看一个代码示例:

def find_min(nums):
    """
    Finds the minimum value in a rotated sorted array.

    Args:
        nums: A rotated sorted array.

    Returns:
        The minimum value in the array.
    """

    left, right = 0, len(nums) - 1

    while left < right:
        mid = (left + right) // 2

        if nums[mid] < nums[right]:
            right = mid - 1
        elif nums[mid] > nums[right]:
            left = mid + 1
        else:
            left += 1
            right -= 1

    return nums[left]


if __name__ == "__main__":
    nums = [4, 5, 6, 7, 0, 1, 2]
    print(find_min(nums))

这个代码的时间复杂度是O(logn)。

总结

在这篇文章中,我们讨论了如何找出旋转排序数组中的最小值。我们介绍了旋转二分查找的算法,并提供了一些示例来帮助理解。