进阶实验1-3.1 两个有序序列的中位数
参考学习:进阶实验1-3.1 两个有序序列的中位数 (25分)
本文为CSDN博主「5?li」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wulila/article/details/108036788
题目
已知有两个等长的非降序序列S1, S2, 设计函数求S1与S2并集的中位数。有序序列A0,A1,⋯,AN−1的中位数指A(N−1)/2的值,即第⌊(N+1)/2⌋个数(A0为第1个数)。
输入格式:
输入分三行。第一行给出序列的公共长度N(0<N≤100000),随后每行输入一个序列的信息,即N个非降序排列的整数。数字用空格间隔。
输出格式:
在一行中输出两个输入序列的并集序列的中位数。
算法0:合并到一个大数组,分治策略的二分归并排序 O ( 2 ∗ N + N l o g 2 N ) ) O(2*N+Nlog_2^N)) O(2∗N+Nlog2N))
没有实现,只是学习思想
输入:按照递增顺序排好序的数组 A [ p . . q ] A[p..q] A[p..q]与 A [ q + 1.. r ] A[q+1..r] A[q+1..r]
输出:按照递增顺序排好序的数组 A [ p . . r ] A[p..r] A[p..r]
void MergeSort(A,p,r)
{
if(p<r)
q=(p+r)/2
MergeSort(A,p,q);
MergeSort(A,q+1,r);
Merge(A,p,q,r);
}
Merge()函数的具体实现
void Merge(int A[],int p,int q,int r)
{
int x=q-p+1,y=r-q;
int i,j,k;
for(i=0;i<x;++i)//将A[p..q]复制到B[1..x]
B[i]=A[i+p-1];
for(j=0;j<y;++j)//将A[q+1..r]复制到C[1..y]
C[j]=A[q+j];
i=j=1;k=p;
while(i<=x&&j<=y)
{
if(B[i]<=C[j])
{
A[k]=B[i];
++i;
}
else
{
A[k]=C[j];
++j;
}
++k;
}
if(i>x)//B已是空数组
{
for(i=j;i<=y;++i)
{A[k]=C[i];++k}
}
else//C已是空数组
{
for(j=i;j<=x;++j)
{A[k]=B[j];++k}
}
}
算法1:循环顺序数中位数 ( 2 ∗ n − 1 ) / 2 (2*n-1)/2 (2∗n−1)/2个数,然后直接输出中位数
#include <iostream>
using namespace std;
class test
{
private:
int n,i;
int* a;
int* b;
public:
test(int nn)
{
n=nn;
a=new int[n];
b=new int[n];
for(i=0;i<n;++i)
{
cin>>a[i];
}
for(i=0;i<n;++i)
{
cin>>b[i];
}
}
void output()
{
int cnt=(2*n-1)/2;
if(cnt>0)
{
int i,j;
for(i=0,j=0; i<n,j<n;)//两个数组的比较输出
{
if(a[i]<b[j])
{
i++;cnt--;
if (cnt==0) break;
}
else
{
j++;cnt--;
if (cnt==0) break;
}
}
if (a[i]<b[j]) cout<<a[i];
else cout<<b[j];
}
else//特殊处理,输入n=1,直接比较输出
{
if(a[0]<b[0])
cout<<a[0];
else
cout<<b[0];
}
}
};
int main()
{
int N;
cin>>N;
test t(N);
t.output();
return 0;
}
算法2: O ( l o g 2 N ) O(log_2^N) O(log2N) 分治策略:找两个子序列的中位数,递归实现
设定两个序列的左右边界:
序列
S
1
、
S
2
,
l
1
、
r
1
S1、S2,l1、r1
S1、S2,l1、r1分别为序列
S
1
S1
S1的左右边界,
l
2
、
r
2
l2、r2
l2、r2分别为序列
S
2
S2
S2的左右边界。
- 分别求两个序列的中位数,特别的,对于偶序列,第二个序列 S 2 S2 S2的中位数为 S 2 [ ( l 2 + r 2 ) / 2 + 1 ] S2[(l2+r2)/2+1] S2[(l2+r2)/2+1],这样使子问题的数目也相等(使得序列 S 1 S1 S1左(右)边和 S 2 S2 S2右(左)边需要去除的数目个数相等)
- 比较两个中位数,如果相等,则为所求。
否则: - 对较大的中位数所在的序列,去掉其后的部分,对于较小的中位数序列,去掉比这个中位数小的部分。将剩下的等长序列,继续递归调用。
-
注意:递归基1:序列S1和S2均各只剩1个数字,比较两个数字的大小,输出小的那个数为中位数。
递归基2:序列 S 1 S1 S1和 S 2 S2 S2均各剩下2个数字,先比较 S 1 [ l 1 ] S1[l1] S1[l1]和 S 2 [ l 2 ] S2[l2] S2[l2]:若 S 1 [ l 1 ] < S 2 [ l 2 ] S1[l1]<S2[l2] S1[l1]<S2[l2],则中位数是 S 1 [ r 1 ] S1[r1] S1[r1]和 S 2 [ l 2 ] S2[l2] S2[l2]中的小者;若 S 1 [ l 1 ] > S 2 [ l 2 ] S1[l1]>S2[l2] S1[l1]>S2[l2],则中位数是 S 1 [ l 1 ] S1[l1] S1[l1]和 S 2 [ r 2 ] S2[r2] S2[r2]中的小者。
注意问题:
return MidTerm(a,b,mid1,r1,l1,mid2);
return 调用的函数中,数组不加[]
;
注意调用函数,是从0到n-1
。
另外不能使用类实现,在外部main()
函数递归调用,不能使用类内私有变量。
#include <iostream>
using namespace std;
int MidTerm(int a[],int b[],int l1,int r1,int l2,int r2)
{
if(l1==r1)//递归基1
{
if(a[l1]<b[l2])
return a[l1];
else return b[l2];
}
else if(l1+1==r1)//递归基2
{
if(a[l1]<b[l2])
{
if(a[r1]<b[l2]) return a[r1];
else return b[l2];
}
else
{
if(b[r2]<a[l1]) return b[r2];
else return a[l1];
}
}
else
{
int mid1=(l1+r1)/2;
int mid2=(l2+r2+1)/2;
if(a[mid1]==b[mid2])
{
return a[mid1];
}
else
{
if(a[mid1]<b[mid2])
return MidTerm(a,b,mid1,r1,l2,mid2);//递归调用
else
return MidTerm(a,b,l1,mid1,mid2,r2);//递归调用
}
}
}
int main()
{
int n,i;
cin>>n;
int* a;
int* b;
a=new int[n];
b=new int[n];
for(i=0;i<n;++i)
{
cin>>a[i];
}
for(i=0;i<n;++i)
{
cin>>b[i];
}
cout<<MidTerm(a,b,0,n-1,0,n-1);
return 0;
}