4. 寻找两个有序数组的中位数
题目描述:
方法一
将两个数组拼接,然后排序,根据中位数的定义来求中位数。
class Solution:
def findMedianSortedArrays(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: float
"""
nums = nums1 + nums2
nums.sort()
length = len(nums)
if length % 2 == 0:
#如果是个数是偶数,根据中位数的定义
#将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。
#就只能取中间两个数的平均值
return (nums[length // 2 - 1] + nums[(length // 2)])/2
#如果个数是奇数,正好有一个数在中间,将两边的数分成个数相同的集合。
return nums[length // 2]
方法二:
递归法
具体参考的是leetcode官网的参考答案
https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/
下面我用自己的话给予补充解释一遍。
首先重申中位数的定义:将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。
我们有两个有序的数组A(m个元素)、B(n个元素)。
用
i
,
j
i,j
i,j两个下标来分别划分成两部分。
这里以A为例。
那么A一共有m+1种划分方法。
B也同理。
上面的等价条件可以写成
l
e
n
(
l
e
f
t
p
a
r
t
)
=
l
e
n
(
r
i
g
h
t
p
a
r
t
)
len(leftpart)=len(rightpart)
len(leftpart)=len(rightpart)
等价于
i
=
0
m
,
j
=
(
m
+
n
+
1
)
/
/
2
−
i
i=0~m,j=(m+n+1)//2-i
i=0 m,j=(m+n+1)//2−i
注意是地板除,原答案写了/。
关于这部分应该是有问题的。
m+n是偶数,两边的个数都能一样
m+n是奇数的时候不行,左边会比右边多一个。
所以这个条件
l
e
n
(
l
e
f
t
p
a
r
t
)
=
l
e
n
(
r
i
g
h
t
p
a
r
t
)
len(leftpart)=len(rightpart)
len(leftpart)=len(rightpart)不是必要的
后面我会写一个具体的例子。
m
a
x
(
l
e
f
t
p
a
r
t
)
≤
m
i
n
(
r
i
g
h
t
p
a
r
t
)
max(leftpart)≤min(rightpart)
max(leftpart)≤min(rightpart)
等价于
B
[
j
−
1
]
≤
A
[
i
]
且
A
[
i
−
1
]
≤
B
[
j
]
B[j-1]≤A[i]且A[i-1]≤B[j]
B[j−1]≤A[i]且A[i−1]≤B[j]
下面我补充这个条件的由来。
接着就是算法的具体过程。这里答案写的很明白我就不多补充了。
注意这里有n≥m的假定,其实只要想一想就能明白,如果n、m的大小关系是反的,把两个数组颠倒一下就可以了,大的叫B,小的叫A。
举个例子方便大家理解。
这样就能加深对算法的理解了。
接下来时临界值的情况,这里答案说的比较清楚。
也就是出现临界值时,我们关键的判断条件 B [ j − 1 ] ≤ A [ i ] 且 A [ i − 1 ] ≤ B [ j ] B[j-1]≤A[i]且A[i-1]≤B[j] B[j−1]≤A[i]且A[i−1]≤B[j],有时候都不需要判断,或者只要判断其中一边,下面附上代码。
class Solution:
def findMedianSortedArrays(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: float
"""
m, n = len(nums1), len(nums2)
if m > n:
nums1, nums2, m, n = nums2, nums1, n, m
#和前面说的一样,m、n大小关系相反时,只需要将两个数组颠倒一下就可以了。
if n == 0:
raise ValueError
imin, imax, half_len = 0, m, (m + n + 1) // 2
while imin <= imax:
i = (imin + imax) // 2
j = half_len - i
#这里写的时普通情况
if i < m and nums2[j-1] > nums1[i]:
# i 太小,需要增大i
imin = i + 1
elif i > 0 and nums1[i-1] > nums2[j]:
# i 太大,需要减小i
imax = i - 1
else:
# 如果上面两边的判定条件都满足,i直接就是答案
#下面是临界值的情况
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 == 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