查找问题:
- 问题一:寻找两个等长有序序列的中位数
问题描述:对于一个长度为n的有序序列(假设均为升序序列)a[0 … n-1] ,处于中间位置的元素称为a的中位数。
例如,若序列a=(11,13,15,17,19),其中位数为15;
若序列b=(2,4,6,8,24),其中位数为6
两个等长有序序列的中位数是含有它们所有元素的有序序列的中位数,如上a,b序列的中位数为11
分析思路:
对于含有n个,元素的有序序列a[s … t ],
当n为奇数时,中位数出现在m=(s+t)/2;
当n为偶数时,中位数出现在m=(s+t)/2上中位和m=(s+t)/2+1下中位;在这里假设只取上中位
(1)分别求a、b的中位数a[m1]和b[m2]
(2)若a[m1]=b[m2],则a[m1]和b[m2]即为所求中位数
(3)若a[m1]<b[m2],则舍弃a中的前半部分,同时舍弃b的后半部分,两端舍弃部分的长度要相等
(4)若a[m1]>b[m2],则舍弃a中的后半部分,同时舍弃b的前半部分,两端舍弃部分的长度要相等
在保留的两个升序序列中重复上述过程,直到两个序列中只含有一个元素为止,较小者即为所求的中位数。
取前半部分均为a[a … m],取后半部分时(看元素个数),
若为奇数,则(s+t)/2
若为偶数,则(s+t)%2==1,则后半部分为a[m+1 …t ]
若序列a=(11,13,15,17,19),其中位数为15;
若序列b=(2,4,6,8,24),其中位数为6
代码一:
#include<stdio.h>
void prepart(int &s,int &t)
{
int m=(s+t)/2; //前半子序列
t=m;
}
void postpart(int &s,int &t)
{
int m=(s+t)/2; //后半序列
if((s+t)%2==0) //序列中有奇数元素
s=m;
else //序列中有偶数个元素
s=m+1;
}
int mid(int a[],int s1,int t1,int b[],int s2,int t2){
int m1,m2;
if(s1==t1 && s2==t2)
return a[s1]<b[s2]?a[s1]:b[s2]; 只有一个元素返回最小者
else
{
m1=(s1+t1)/2; //求a 、b中位数
m2=(s2+t2)/2;
if(a[m1]==b[m2])
return a[m1];
if(a[m1]<b[m2]){
postpart(s1,t1); //取a后半部分
prepart(s2,t2); //取b前半部分
return mid(a,s1,t1,b,s2,t2);
}
else{
prepart(s1,t1); //取a半部分
postpart(s2,t2); //取b后半部分
return mid(a,s1,t1,b,s2,t2);
}
}
}
int main(){
int a[]={11,13,15,17,19};
int b[]={2,4,6,8,24};
printf("中位数:%d\n",mid(a,0,4,b,0,4));
}
求两个序列中位数可用循环语句来替换,等价的非递归算法
代码二:
#include<stdio.h>
void prepart(int &s,int &t)
{
int m=(s+t)/2; //前半子序列
t=m;
}
void postpart(int &s,int &t)
{
int m=(s+t)/2; //后半序列
if((s+t)%2==0) //序列中有奇数元素
s=m;
else //序列中有偶数个元素
s=m+1;
}
int mid(int a[],int b[],int n){
int s1,t1,m1,s2,t2,m2;
s1=0;
t1=n-1;
s2=0;
t2=n-1;
while(s1!=t1 || s2!=t2){
m1=(s1+t1)/2;
m2=(s2+t2)/2;
if(a[m1]==b[m2])
return a[m1];
if(a[m1]<b[m2]){
postpart(s1,t1);
prepart(s2,t2);
}
else{
prepart(s1,t1);
postpart(s2,t2);
}
}
return a[s1]<b[s2]?a[s1]:b[s2];
}
int main(){
int a[]={11,13,15,17,19};
int b[]={2,4,6,8,24};
printf("中位数:%d\n",mid(a,b,5));
}