算法第二章实践报告
1. 实践题目
7-3 两个有序序列的中位数 (20 分)
已知有两个等长的非降序序列S1, S2, 设计函数求S1与S2并集的中位数。有序序列A0,A1,⋯,AN−1的中位数指A(N−1)/2的值,即第⌊(N+1)/2⌋个数(A0为第1个数)。
2. 问题描述
即求两个有序序列并集后的中位数,由于两个有序序列并集的元素数2n量是偶数,因此即求这两个序列并集后排序好的第n个元素。
3. 算法描述
#include<iostream>
using namespace std;
int main(void)
{
int n;
cin>>n;
int a[100001];
int b[100001];
for(int i=0;i<n;i++)
cin>>a[i];
for(int i=0;i<n;i++)
cin>>b[i];
int k=n-1;
int x=0,y=0;
while(1)
{
int md=k/2;
if(md!=0)
{
if(md%2==0)
{
if(a[x+md]==b[y+md])
{
cout<<a[x+md];
break;
}
else if(a[x+md]>b[y+md])
{
y+=md;
k-=md;
}
else
{
x+=md;
k-=md;
}
}
else
{
if(a[x+md]==b[y+md])
{
cout<<a[x+md];
break;
}
else if(a[x+md]>b[y+md])
{
y+=md+1;
k-=md+1;
}
else
{
x+=md+1;
k-=md+1;
}
}
}
else
{
if(k==0)
{
if(a[x]==b[y])
{
cout<<a[x];
break;
}
else if(a[x]>b[y])
{
cout<<b[y];
break;
}
else
{
cout<<a[x];
break;
}
}
if(k==1)
{
if(a[x]==b[y])
{
cout<<a[x];
break;
}
else if(a[x]>b[y])
{
y+=1;
k-=1;
}
else
{
x+=1;
k-=1;
}
}
}
}
return 0;
}
4. 算法时间及空间复杂度分析
求两个数列并集后的有序的第n个元素,注意到第n个元素等价于前面有n-1个元素不大于它,符合这个条件的最小数即所求数。
我们先取两个数组,各自元素为题给两个数列的元素,计所求元素前面还有k个比它小的。
初始情况下k为n-1,取k/2,则先比较每个数组的第1+k/2个元素,如果a数组的第1+k/2个元素小于b数组的第1+k/2个元素,则a数组的前k/2个元素都可直接去掉,因为它们绝对不可能满足有k个数比它们小。
然后我们已经去掉了k/2个数,重新得到数组a和数组b,并且此时k为k-k/2(注意不一定等于k/2),这样问题就缩减为原来的一半规模了。直至k为0,此时比较a和b数组的首元素,谁小谁就是所求的中位数。
因此时间复杂度与二分搜索的一样,每次都使问题规模缩减一半,即logn。
空间复杂度为o(n),即保存两个数组元素的空间需求。
5. 心得体会(对本次实践收获及疑惑进行总结)
在做编程题目的时候,应该先把题目条件认真看完,例如题目中的中位数与我们以往学习的中位数概念有所偏差。
然后应该从题目条件开始入手,再结合算法思想完成题目。
在本次实践中,对二分搜索算法有了进一步的认识以及基本上熟识了二分算法的思想。