返回

LeetCode #81 搜索旋转排序数组 II:Python 解决方案

IOS

探索 LeetCode 81:搜索旋转排序数组 II,解决重复元素难题

引言

在算法世界中,解决难题可以激发创造力并磨练解决问题的技巧。LeetCode 81:搜索旋转排序数组 II 就是这样一个难题,它挑战我们找到一个目标元素,即使在数组被旋转并可能包含重复元素的情况下也是如此。

理解问题

让我们深入了解一下 LeetCode 81 问题。我们给定一个整数数组 nums 和一个目标值 target。这个数组很特别,因为它可能被旋转过。这意味着它的一部分可能被移动到数组的另一端。想象一下一个旋转的轮子,数组就像轮子上的数字,旋转后数字的顺序会发生变化。

进一步复杂化的是,数组中可能包含重复的元素。这使得直接使用二分查找变得不可能,因为重复的元素会破坏二分查找的二分性质。

二分查找的限制

传统的二分查找算法假设数组是有序的,并且没有重复的元素。通过比较目标元素和中间元素,算法可以快速缩小搜索范围。然而,对于包含重复元素的旋转数组,这种方法会失败。这是因为重复的元素会产生歧义,无法明确确定中间元素的归属区间。

解决方法:修改二分查找

为了解决这个问题,我们需要修改二分查找算法。关键思想是识别数组中的两种情况:有序部分和旋转部分。通过仔细比较这些部分的边界,我们可以消除歧义并逐步缩小搜索范围。

修改后的二分查找算法步骤

以下是修改后的二分查找算法的步骤:

  1. 初始化左右指针 leftright 分别为 0 和 nums.length-1
  2. 进入循环,直到 left 不大于 right
  3. 计算 mid(left + right) // 2
  4. 检查 nums[mid] 是否等于 target。如果是,返回 True
  5. 如果 nums[left] 等于 nums[mid],表示存在重复元素,将 left 右移一步。
  6. 否则,如果 nums[left] 小于 nums[mid],表示左半部分有序,将 right 移至 mid-1
  7. 否则,表示右半部分有序,将 left 移至 mid+1
  8. 循环结束后,返回 False

示例:

考虑数组 nums = [1, 3, 1, 1, 1]target = 3

  • 初始化:left = 0right = 4
  • 循环 1:mid = (0 + 4) // 2 = 2nums[2] = 1,不等于 target
  • 由于 nums[left] = nums[mid] = 1,将 left 右移。left = 1
  • 循环 2:mid = (1 + 4) // 2 = 2nums[2] = 1,不等于 target
  • 由于 nums[left] = nums[mid] = 1,将 left 右移。left = 2
  • 循环 3:mid = (2 + 4) // 2 = 3nums[3] = 1,不等于 target
  • 由于 nums[left] = nums[mid] = 1,将 left 右移。left = 3
  • 循环 4:mid = (3 + 4) // 2 = 3nums[3] = 1,不等于 target
  • 由于 nums[left] = nums[mid] = 1,将 left 右移。left = 4
  • 循环 5:left = 5 不大于 right = 4。退出循环。
  • 返回 False

在这个示例中,target = 3 不在数组中,因此算法返回 False

代码示例:

def search(nums, target):
  left, right = 0, len(nums) - 1

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

    if nums[mid] == target:
      return True

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

  return False

结论

修改后的二分查找算法有效地解决了 LeetCode 81:搜索旋转排序数组 II 问题,即使数组中包含重复的元素。它通过识别数组中的有序部分和旋转部分,并通过仔细比较边界来消除歧义,逐步缩小搜索范围。

常见问题解答

1. 修改后的二分查找算法与传统二分查找算法有何不同?

修改后的算法引入了对重复元素的处理,并通过检查数组的边界来识别有序和旋转部分。

2. 为什么在检测到重复元素时要右移 left 指针?

右移 left 指针会跳过当前的重复元素,因为无法确定它们所属的区间。

3. 算法如何确定有序部分和旋转部分?

通过比较数组边界的相对大小,算法可以推断出哪个部分是有序的,哪个部分是旋转的。

4. 如果数组中没有重复的元素,算法的表现如何?

算法简化为传统二分查找,因为它不再需要处理重复元素。

5. 算法的时间复杂度是多少?

算法的时间复杂度仍然是 O(log n),其中 n 是数组的大小,与传统二分查找相同。