分治法寻找两个有序数组的中位数
题意
给定两个大小为 m 和 n 的有序数组A 和 B。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
保证A 和 B 不会同时为空。
样例1
A = [1, 3]
B= [2]
中位数是 2.0
样例2
A = [1, 2]
B = [3, 4]
中位数是 (2 + 3)/2 = 2.5
单个数组
我们先简化问题,如何求出单个数组的中位数?
A[0] | A[1] | … | A[m-1] |
---|
对于一个数组可以选择两个位置进行分割,使得两边元素个数相等。对于任意偶数个元素必有如下表所示的分割使得两边元素个数相等。
left | mid | right |
---|---|---|
A[0], A[1], …,A[i-2] | A[i-1] , A[i] | A[i+1], …, A[m-1] |
同样,对于任意奇数个元素必有如下图所示的分割。
left | mid | right |
---|---|---|
A[0], A[1], …,A[i-1] | A[i] | A[i+1], …, A[m-1] |
偶数个元素分割示例:
1 | 2 | 4 | 5 | 6 | 7 | 8 | 10 |
---|
left | mid | right |
---|---|---|
1 , 2, 4 | 5, 6 | 7, 8, 10 |
中位数是(5 + 6)/2 = 5.5
奇数个元素分割示例:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|
left | mid | right |
---|---|---|
1 , 2, 3, 4 | 5 | 6, 7, 8, 9 |
中位数是5
两个等长数组
接下来将单个数组拆分成两个长度相等的数组。我们可以在A中确定位置i,B中确定位置j,使得左右两侧元素个数相等。1
left | mid | right |
---|---|---|
A[0], A[1], …, A[i-2] | A[i-1], A[i] | A[i+1], …, A[n-1] |
B[0], B[1], …, B[j-2] | B[j-1], B[j] | B[j+1], …, B[n-1] |
left | mid | right |
---|---|---|
A[0], A[1], …, A[i-1] | A[i] | A[i+1], …, A[n-1] |
B[0], B[1], …, B[j-1] | B[j] | B[j+1], …, B[n-1] |
我们选取偶数个元素的情况进行说明,奇数个元素与此类似,只需要在计算中位数的函数部分稍加改动。2
此时一定有
- (i-1)-0+1+(j-1)-0+1 =(n-1)-i+1+(n-1)-j+1 => i + j = n - i + n - j
- B[j-1] <= A[i] && A[i-1] <= B[j]
我们就可以通过这四个数字得到中位数了。可是我们如何找到i,j呢?
由于A、B长度一定,故只要确定i的位置,j的位置可以随之确定
- 分别计算两个数组的中位数;
- 若midA = midB ,则midA就是整个数组的中位数(思考一下为什么),返回midA;
- 若midA < midB, 则中位数一定出现在数组B的左半部分和数组A的右半部分。
保留A |_ (n-1)/2 | ,…, A[n-1],将新得到的数组作为A;
保留B [0], B[1], …, B[ n- | (n-1)/2 _| ]),将新得到的数组作为B; - 若midA > midB, 则中位数一定出现在数组A的左半部分和数组B的右半部分。
保留A [0], A[1], … A[n-|_ (n-1)/2 |],将新得到的数组作为A;
保留B| (n-1)/2 _ | … B[n-1],将新得到的数组作为B;
(n-1)/2向下取整,这样能把参与中位数计算的较小的数保留下来;
n - | _ (n-1)/2 _|向上取整,这样能把参与中位数计算的较大的数保留下来。
- 直到B[j-1] <= A[i] && A[i-1] <= B[j],返回average(max(A[i-1], B[j-1]) + min(A[i], B[j])) / 2。
array | 1 | 2 | 3 | 4 |
---|---|---|---|---|
A | 1 | 5 | 6 | 10 |
B | 2 | 4 | 8 | 8 |
midA = (5 + 6) / 2 = 5.5 , midB = (4 + 8) / 2 = 6
midA < midB,取B的左半部分和A的右半部分
array | 1 | 2 | 3 | 4 |
---|---|---|---|---|
A | * | 5 | 6 | 10 |
B | 2 | 4 | 8 | * |
mid = (5 + 6) / 2 = 5.5。
可以参考一下geeksforgeeks Median of two sorted arrays of same size这篇文章给出的的代码。
// A divide and conquer based
// efficient solution to find
// median of two sorted arrays
// of same size.
#include<bits/stdc++.h>
using namespace std;
/* to get median of a
sorted array */
int median(int [], int);
/* This function returns median
of ar1[] and ar2[].
Assumptions in this function:
Both ar1[] and ar2[] are
sorted arrays
Both have n elements */
int getMedian(int ar1[],