查找两个等长有序序列的中位数问题
问题描述:给定两个等长的有序序列,找到这两个序列的中位数。
分治法思路:
- 分解:将两个序列分别分解为较小的子序列。
- 解决:递归地在子序列中找到中位数。
- 合并:将子序列的中位数合并,得到整个序列的中位数。
例如,若序列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; (头+尾) 类似mid=(low+high)/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 ]
C语言代码及注释
#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));
}
时间复杂度分析
查找两个有序序列的中位数问题的时间复杂度主要取决于二分查找的效率。每次二分查找的时间复杂度为[O(log n)],其中[n]是较小数组的长度。
- 最佳情况:每次二分查找都能均匀地将数组分为两部分,时间复杂度为[O(log n)]。
- 平均情况:在平均情况下,时间复杂度为[O(log n)]。
- 最坏情况:如果每次二分查找都不能很好地将数组分为两部分,时间复杂度为[O(log n)]。
整个算法的平均时间复杂度为[O(log n)]