本文内容摘录于leetcode,图解请查看以下网址:
https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/xun-zhao-liang-ge-you-xu-shu-zu-de-zhong-wei-s-114/
题目
寻找两个正序数组的中位数
解答
# LeetCode 二分法 官方解答整理
# 若数组的长度 n 为奇数,则中位数就是下标为 n/2 的值,若 n 为偶数,那么中位数就是下标为 n/2 和 (n/2+1) 的和的平均值
# 所以问题可以转化为,对于一个给定的数 k ,如何快速有效的实现找到第 k 个小的数,即拥有 k-1 个小于等于自己的数
def get_median(a,b):
if (len(a)+len(b))%2 == 1 :
return (find_k(a,b,(int)((len(a)+len(b))/2 + 1)))
else:
return (find_k(a,b,(int)((len(a)+len(b))/2)) + find_k(a, b, (int)((len(a)+len(b))/ 2 + 1)))/2
# 我们可以使用 (k/2-1) 作为关键性下标,去寻找数组中第 k 小的数
# - A 中存在 k/2-1 个比 A[k/2-1] 小的数,B 中存在 k/2-1 个比 B[k/2-1]
# - 记录 Ak = A[k/2-1] ,Bk = B[k/2-1] 根据以下判断:
# -- 1.Ak < Bk ,A 中存在 k/2-1 个比 Ak 小的数,B中最多存在 k/2-1 个比 Ak 小的数
# -- 所以累计最多有 k-2 个比 Ak 小的数,所以 A[0] - A[k/2-1] 都不可能是第 k 小的数,可以全部排除
# -- 2.同理,若Ak > Bk ,可以全部排除 B[0] - B[k/2-1]
# -- 3.Ak = Bk ,可以任选以上两种情形处理
# - 在排除了 k/2 个数字之后,我们原来想找的第 k 个小的数.在去除被排除的数之后,变为寻找第 k/2 个小的数
# - 重复以上逻辑直到找到目标值
def find_k(a, b, k):
a_length = len(a)
b_length = len(b)
# 记录数组 a 已经排除的长度
exclude_a_len = 0
# 记录数组 b 已经排除的长度
exclude_b_len = 0
# 循环
while True:
# 边界处理
# 当数组 a 排除的数组长度已经达到 a 的总长度时,说明第 k 个小的数在 b 中
if exclude_a_len == a_length:
return b[exclude_b_len+k-1]
# 当数组 b 排除的数组长度已经达到 b 的总长度时,说明第 k 个小的数在 a 中
if exclude_b_len == b_length:
return a[exclude_a_len + k - 1]
# 如果 k 是 1 ,那么就去两个数组中,获取除了已经排除的数字之外的最小值
if k==1:
# 数组a,b已经是有序的
return min(a[exclude_a_len],b[exclude_b_len])
# 排除数字之后的第 k/2 个小的数的位置和数组长度作比较,取最小值,避免超出下标范围
half = (int)(k / 2)
a_index = min(exclude_a_len + half, a_length)-1
b_index = min(exclude_b_len + half, b_length)-1
# 数组 a 中第k/2个数
a_half = a[a_index]
# 数组 b 中第k/2个数
b_half = b[b_index]
if a_half <= b_half:
# 情况 1 3
# 更新要找的数字的位置
k -= (a_index + 1 - exclude_a_len)
# 更新数组 a 排除的长度
exclude_a_len = a_index + 1
else:
# 情况2
k -= (b_index + 1 - exclude_b_len)
exclude_b_len = b_index + 1