007's Studio.

算法导论练习9.3-8两个有序数组的中位数

字数统计: 1.3k阅读时长: 6 min
2019/08/01 Share

9.3-8 设X[1..n]和Y[1..n]为两个数组,每个都包含n个有序的元素,请设计一个$O(lgn)$时间的算法找出数组X和Y中所有2n个元素的中位数。

下面假设中位数(低中位数,数组长度偶数时较小的那个)在数组$X$中。如果要求中间两个数的均值,只需要在该基础之上修改一点就好。

a、$X[k]=m$为中位数,对于数组$X$,有$k$个元素小于等于$X[k]$,同时有$n-k$个元素大于等于$X[k]$。当两个数组合并有序时,对于中位数来说,共有$n$个元素小于等于$X[k]$,n个元素大于等于$X[k]$。那么对于数组$Y$来说,有$n-k$个元素小于等于$X[k]$,$n-(n-k)=k$个元素大于等于$X[k]$。那么就存在以下不等式:

b、$若X[k]$不是中位数。且中位数为$X[k’] \,(1 \leq k’ < k < n)$,那么就有:

那么对于$X[k]$来说,有:

c、$若X[k]$不是中位数。且中位数为$X[k’] (1 \leq k < k’ < n)$,那么就有:

那么对于$X[k]$来说,有:

d、在递归的过程中,我们要注意一个递归的一个边界问题,在实际的编程中,对于数组$A[L,..R],1 \leq L \leq R \leq n$。我们取$k=(L+R)/2,k \in [1,n]$, 当$k=n$时,那么对于上面三种情况会出现$Y[n-k]=Y[0]$是一个空数组,对于这种情况,其实只需判断$X[k]=X[n] \leq Y[1]$。

利用上面的性质,就可以在$O(1)$的时间内判断$X[k]$是否时中位数。对于一个数组区间$X[L,R]$。我们采用二分的思想,取中点$k=(L+R)/2$,判断上面的条件,如果满足条件a或d,那么$X[k]$就是中位数,如果是条件b,那么说明$k$偏大,我们在左子区间继续二分查找,否则为条件c,我们在右子区间继续二分查找。这样的一个二分查找过程的时间复杂度为$\Theta(lgn)$。

上面给出性质的是假设中位数在数组$X$的分析,如果中位数不在数组$X$中,在查找的过程中到达边界而不满足$X[k]$为中位数的a条件,我们可以返回一个标志如NOTFIND。此时中位数一定在数组$Y$中,我们进行同样的操作。最差情况下进行$2lgn$次查找。时间复杂度仍为为$\Theta(lgn)$。

下面是伪代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Two-Array-Medium(X,Y)
n <-- X.length //与数组Y长度相同
medium = Find-Medium(X,Y,1,n,n)//先尝试在数组X查找
if medium = NOTFIND//未找到,在Y数组查找
medium = Find-Medium(Y,X,1,n,n)
return medium

Find-Medium(A,B,low,high,n)
if low > high
return NOTFIND
else
k = (high+low)/2
if k=n and A[n]<=B[1]
return A[n]
elseif k < n and B[n-k] <= A[k] <= B[n-k+1]
return A[k]
elseif B[n-k+1] < A[k]
return Find-Medium(A,B,low,k-1,n)
else
return Find-Medium(A,B,k+1,high,n)

实际编程中,数组从0开始稍微改动下即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include<iostream>
#include<ctime>
#include<random>
#include<algorithm>
using namespace std;
#define LEN 5
#define NOTFIND 0x3f3f3f3f

//参数n为数组长度,此时边界为n-1
int findMedium(int A[],int B[],int L,int R,int n)
{
if(L>R)
return NOTFIND;
int k=(L+R)/2;
// k=n-1时要独立一个分支,否则第二个if会越界
if(k==(n-1)&&A[k]<=B[0])
return A[k];
else if(k<(n-1)&&B[n-k-2]<=A[k]&&A[k]<=B[n-k-1])
return A[k];
else if(B[n-k-1]<A[k])
return findMedium(A,B,L,k-1,n);
else
return findMedium(A,B,k+1,R,n);
}
int TwoArrayMedium(int A[],int B[],int len)
{
int ret=NOTFIND;
if((ret=findMedium(A,B,0,len-1,len))==NOTFIND)
ret=findMedium(B,A,0,len-1,len);//不在数组A,继续在B中查找
return ret;
}
template<typename T,unsigned N>
void getRandomArray(T (&arr)[N])
{
//随机生成2000以内的随机数
static default_random_engine e(time(nullptr));
static uniform_int_distribution<T> d(0,2000);
for(auto &i:arr)
i=d(e);
sort(arr,arr+N);//数组排序
}
int main()
{
int A[LEN];
int B[LEN];
getRandomArray(A);
getRandomArray(B);
int C[2*LEN];
copy(A,A+LEN,C);
copy(B,B+LEN,C+LEN);
sort(C,C+2*LEN);
for(auto i:C)
cout << i << " ";
cout << endl;
cout << "中位数为:" << TwoArrayMedium(A,B,LEN) << endl;

return 0;
}

  这个问题我只求了低中位数,因为数组总长度为$2n$恒为偶数,因此如果要求中间两个数的均值。只需要在上面代码的基础上稍微改下,加上$k+1$位置的数,然后除2即可达到。(注意:如果k求出是X数组的最后一位,那么高中位数在数组Y中)

原文作者:007havegone

原文链接:http://007havegone.github.io/2019/08/01/算法导论练习9-3-8两个有序数组的中位数/

发表日期:August 1st 2019, 6:16:30 pm

更新日期:August 18th 2019, 9:36:55 pm

版权声明:‘原创内容,转载时请附上出处’

CATALOG
  1. 1. 9.3-8 设X[1..n]和Y[1..n]为两个数组,每个都包含n个有序的元素,请设计一个$O(lgn)$时间的算法找出数组X和Y中所有2n个元素的中位数。