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

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

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

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

Y [ n − k ] ≤ X [ k ] ≤ Y [ n − k + 1 ] , 1 ≤ k &lt; n Y[n-k] \leq X[k] \leq Y[n-k+1],1 \leq k &lt; n Y[nk]X[k]Y[nk+1],1k<n

b、 若 X [ k ] 若X[k] X[k]不是中位数。且中位数为 X [ k ′ ] &ThinSpace; ( 1 ≤ k ′ &lt; k &lt; n ) X[k&#x27;] \,(1 \leq k&#x27; &lt; k &lt; n) X[k](1k<k<n),那么就有:

Y [ n − k ′ ] ≤ X [ k ′ ] ≤ Y [ n − k ′ + 1 ] Y[n-k&#x27;] \leq X[k&#x27;] \leq Y[n-k&#x27;+1] Y[nk]X[k]Y[nk+1]

那么对于 X [ k ] X[k] X[k]来说,有:
Y [ n − k + 1 ] ≤ Y [ n − k ′ ] ≤ X [ k ′ ] &lt; X [ k ] Y [ n − k + 1 ] &lt; X [ k ] \begin{aligned} Y[n-k+1] &amp;\leq Y[n-k&#x27;] \leq X[k&#x27;] &lt; X[k] \\ Y[n-k+1] &amp;&lt; X[k] \end{aligned} Y[nk+1]Y[nk+1]Y[nk]X[k]<X[k]<X[k]

c、 若 X [ k ] 若X[k] X[k]不是中位数。且中位数为 X [ k ′ ]   ( 1 ≤ k &lt; k ′ &lt; n ) X[k&#x27;] \ (1 \leq k &lt; k&#x27; &lt; n) X[k] (1k<k<n),那么就有:
Y [ n − k ′ ] ≤ X [ k ′ ] ≤ Y [ n − k ′ + 1 ] Y[n-k&#x27;] \leq X[k&#x27;] \leq Y[n-k&#x27;+1] Y[nk]X[k]Y[nk+1]

那么对于 X [ k ] X[k] X[k]来说,有:
X [ k ] &lt; X [ k ′ ] ≤ Y [ n − k ′ + 1 ] ≤ Y [ n − k ] X [ k ] &lt; Y [ n − k ] \begin{aligned} X[k] &amp;&lt; X[k&#x27;] \leq Y[n-k&#x27;+1] \leq Y[n-k] \\ X[k] &amp;&lt; Y[n-k] \end{aligned} X[k]X[k]<X[k]Y[nk+1]Y[nk]<Y[nk]

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

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

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

下面是伪代码:

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开始稍微改动下即可。

#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;
}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值