题目来源:leetcode
归并(复杂度不满足要求)
若题目没有要求时间复杂度,对于两个有序数组,我们很容易想到用双指针归并的思想解决,此时复杂度为O(m+n),代码如下:
class Solution:
# o(m+n)解法
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
m, n = len(nums1), len(nums2)
res = []
i = j = 0
while i < m and j < n:
if nums1[i] < nums2[j]:
res.append(nums1[i])
i += 1
else:
res.append(nums2[j])
j += 1
if i == m:
res += nums2[j:n]
if j == n:
res += nums1[i:m]
if len(res) % 2 == 1:
return res[len(res) // 2]
else:
return (res[len(res) // 2] + res[len(res) // 2 - 1]) / 2
利用双指针一次比较nums1和nums2中的每个元素,当其中一个数组遍历完成后我们将另一个数组的剩余部分直接加到新数组后面即可。这样,两个有序的数组就被我们合并成了一个有序的数组,对于一个有序的数组,我们求其中位数很简单,只需区分奇偶情况即可。
二分
题目要求在O(log(m+n))复杂度下完成,看到log级,且有序数组,此时我们应该考虑二分法查找其中的一个数组。代码如下:
def findMedianSortedArrays1(self, nums1: List[int], nums2: List[int]) -> float:
m, n = len(nums1), len(nums2)
# 交换顺序,使得m总是指向最短的那个数组
if m > n:
nums1, m, nums2, n = nums2, n, nums1, m
if n == 0:
raise ValueError
'''
我们来考虑一个已合并的数组,求其中位数,显然有2种情况:
1.数组长度为奇数,中位数就等于中间的那个元素
2.数组长度为偶数,中位数等于中间两个数之和除以2
所以,我们在这里定义half_pos,值为m+n+1,
由于//2为向下取整,所以这样可以将奇偶两种情况合并。
'''
l, r, half_pos = 0, m, (m + n + 1) // 2
while l <= r:
# 在m中二分查找
i = l + (r - l) // 2
# 因为m中取了i,所以j中只需对half_pos - i的位置进行考虑即可
j = half_pos - i
'''
我们将两个数组分为2个部分,分别为左半部分[0:i] + [0:j] 与 右半部分[i:] + [j:],
显然这符合中位数的定义,将数组分为左右元素个数相等的两部分
在次数我们定义的i,j在偶数情况下,左右两半部分数量相等,
奇数情况下,左半部分比右半部分多出一个元素
'''
# i不在左边界,因为nums1为排序数组,显然nums1[i-1] < nums1[i],所以无需比较
# 若nums1[i-1]>nums2[j],显然此时的i取得大了
# 所以利用二分搜索i的左半区间
if i > 0 and nums1[i-1] > nums2[j]:
r = i - 1
# 同上
elif i < m and nums1[i] < nums2[j-1]:
l = i + 1
else:
# i取到了nums1的左边界,此时nums1的左边没有元素了,显然此时左半部分的最大值是nums2[j-1]
if i == 0:
max_of_left = nums2[j-1]
# 同上
elif j == 0:
max_of_left = nums1[i-1]
# i,j均不为0,此时,i,j都取在了中间的位置,那么左半部分最大值为max(nums1[i-1], nums2[j-1])
else:
max_of_left = max(nums1[i-1], nums2[j-1])
# 若该数组长度为奇数,直接返回max_of_left即可
if (m + n) % 2 == 1:
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])
# 返回两个数之和的一半
return (max_of_left + min_of_right) / 2.0
画图不太方便,所以只用了文字描述,描述可能不太清晰,建议读者在纸上画出示意图帮助理解