LeetCod-4-两个排序数组的中位数

Posted by 刘知安 on 2018-11-15
文章目录
  1. 题目内容
    1. 描述
    2. 示例:
  2. 插入一段废话
  • 思路
  • code
    1. 要注意的地方
  • 推广
  • 题目内容

    描述

    给定两个大小为 m 和 n 的有序数组 nums1 和 nums2 。

    请找出这两个有序数组的中位数。要求算法的时间复杂度为 O(log (m+n)) 。

    你可以假设 nums1 和 nums2 均不为空。

    示例:

    • 示例1
    1
    2
    3
    4
    nums1 = [1, 3]
    nums2 = [2]

    中位数是 2.0
    • 示例2
    1
    2
    3
    4
    nums1 = [1, 2]
    nums2 = [3, 4]

    中位数是 (2 + 3)/2 = 2.5

    插入一段废话

    首先看到这个O(log (m+n)) ,肯定会想到折半查找,可是我开始想错了方向,我想的是运用“双指针”思想,给nums1和nums2各设两个指针,再通过比大小和指针的移动,从而找到一个列表在另一个列表的合适插入位置。。。尼玛。这样是不对的,譬如有[1,2,4]和[2,4,5],这样就根本无法插入。

    于是想到如何拿到合并后(排好序的)的前一半数,当然最简单的,直接再开一个列表,然后两个列表各给一个头指针,谁小谁就被放进新的列表,然后指针移动,但这样就是O(m+n)了,这个二分无奈没想出来,看完提示,豁然开朗,真是个好想法。

    思路

    https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/ 讲的非常详细非常好

    code

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    class Solution:
    def findMedianSortedArrays(self, nums1, nums2):
    """
    :type nums1: List[int]
    :type nums2: List[int]
    :rtype: float
    """
    m, n = len(nums1), len(nums2)
    # 使得n>=m,确保下标j不为负数
    if n < m:
    m, n = n, m
    nums1, nums2 = nums2, nums1
    # 二分查找的上下限
    index_min, index_max = 0, m
    while index_min <= index_max:
    # 二分寻找满足条件的i
    i = int((index_min + index_max) / 2)
    # j是根据长度公式推导出
    j = int((m + n + 1) / 2 - i)

    # 情况①
    if i > 0 and j < n and nums1[i - 1] > nums2[j]:
    index_max -= 1
    # 情况②
    elif i < m and j > 0 and nums2[j - 1] > nums1[i]:
    index_min += 1

    # 当前的 i,j 刚好满足条件,直接返回结果(需要奇偶判断一下)
    else:
    # 左边最大
    if i == 0:
    max_of_left = nums2[j - 1]
    elif j == 0:
    max_of_left = nums1[i - 1]
    else:
    max_of_left = max(nums1[i - 1], nums2[j - 1])
    # 是奇数
    if (m + n) % 2 != 0:
    return max_of_left

    # 右边最小
    if i == m:
    min_of_right = nums2[j]
    elif j == n:
    min_of_right = nums1[i]
    else:
    min_of_right = min(nums1[i], nums2[j])

    # 是偶数
    if (m + n) % 2 == 0:
    return float(max_of_left + min_of_right) / 2

    要注意的地方

    • 数组下标越界(折磨了我好一会儿)

      1
      2
      3
      4
      5
      6
      # 情况①
      if i > 0 and j < n and nums1[i - 1] > nums2[j]:
      index_max -= 1
      # 情况②
      elif i < m and j > 0 and nums2[j - 1] > nums1[i]:
      index_min += 1

      反正把这个下标限定死,不要让它越界就好了。官网的解答上说有两个条件可以由另外两个条件推导出,但我觉得写上去更容易理解吧

    • 返回时if和elif的判断位置
      如果已经知道是奇数个数了,那就不要再求min_of_right了,也是考虑到会出现数组越界

    推广

    两个有序数组中的Top K问题同样可以用这个思路求解。