题目
给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的中位数。
进阶:你能设计一个时间复杂度为 O(log (m+n)) 的算法解决此问题吗?
示例
nums1 = [1, 2]nums2 = [3, 4]则中位数是 (2 + 3)/2 = 2.5
实现
思路1
直接把两个有序数组合并为一个有序数组,然后根据数组长度来确定中位数,如果数组长度为偶数,那么返回两个中位数的平均值,如果数组长度为奇数,那么返回中位数。
class Solution(object):def findMedianSortedArrays(self, nums1, nums2):""":type nums1: List[int]:type nums2: List[int]:rtype: float"""nums1.extend(nums2)nums1.sort()length = len(nums1)if length & 1: # 如果是奇数,返回中位数return nums1[(length - 1) >> 1]else: # 返回两个中位数的平均值return (nums1[(length-1) >> 1] + nums1[length >> 1]) / 2.0
这里补充一下利用位运算实现快速计算
# 通过<<, >> 快速计算2的倍数问题n << 1 -> 计算 n*2n >> 1 -> 计算 n/2,负奇数的运算不可用n << m -> 计算 n*(2^m),即乘以 2 的 m 次方n >> m -> 计算 n/(2^m),即除以 2 的 m 次方1 << n -> 2^n
思路2
首先我们要知道中位数的作用是:将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。
因此我们可以在任意位置 i 将数组 A 划分为两部分:
left_A | right_AA[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1]
这里A中有m个元素,所以有m+1种划分的方法。其中
然后可以采用同样的方式在任意位置 j 将数组 B 划分成两个部分:
left_B | right_BB[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[n-1]
然后我们将 和
放入一个集合,并将
和
放入另一个集合。 再把这两个新的集合分别命名为 left_part 和 right_part:
left_part | right_partA[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1]B[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[n-1]
- 当A和B的总长度为偶数时,如果
,即 所有元素已被划分为相同长度两个部分;
,即 前一部分中的元素总是小于或等于后一部分中的元素
那么中位数就是前一部分的最大值和后一部分的最小值的平均值,即
- 当A和B的总长度为奇数时,如果
,即 前一部分比后一部分多一个元素;
,即 前一部分中的元素总是小于或等于后一部分中的元素
那么中位数就是前一部分的最大值,即
class Solution:def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:if len(nums1) > len(nums2):return self.findMedianSortedArrays(nums2, nums1)infinty = 2**40m, n = len(nums1), len(nums2)left, right = 0, m# median1:前一部分的最大值# median2:后一部分的最小值median1, median2 = 0, 0while left <= right:# 前一部分包含 nums1[0 .. i-1] 和 nums2[0 .. j-1]# // 后一部分包含 nums1[i .. m-1] 和 nums2[j .. n-1]i = (left + right) // 2j = (m + n + 1) // 2 - i# nums_im1, nums_i, nums_jm1, nums_j 分别表示 nums1[i-1], nums1[i], nums2[j-1], nums2[j]nums_im1 = (-infinty if i == 0 else nums1[i - 1])nums_i = (infinty if i == m else nums1[i])nums_jm1 = (-infinty if j == 0 else nums2[j - 1])nums_j = (infinty if j == n else nums2[j])if nums_im1 <= nums_j:median1, median2 = max(nums_im1, nums_jm1), min(nums_i, nums_j)left = i + 1else:right = i - 1return (median1 + median2) / 2 if (m + n) % 2 == 0 else median1
