(2011年真题)长度为L的升序序列S,在第(L/2)个位置的数称为S的中位数,注意:向上取整,也就是说L=5,则第三个是中位数,两个序列的中位数是指两个序列合并后的的中位数。现给出等长升序序列A和B,试着设计算法,找出A和B的中位数
- 1)给出算法的设计思路
- 2)用c/c++/java描述算法
- 3)说明空间复杂度和时间复杂度
这个题利用二分查找做,我觉得答案的算法还是比较容易想明白的
但是如果要讲的话,不太好讲,所以解析写的不太好,凑合看,大概就是这么个意思~
解析:(若不想看解析,直接拉到下边是答案)
这个问题可能很多人会想到一种方法,因为是升序序列,两个序列合并后长度是2L,我们边扫描边比较并计数,需要比较L次即可得到中位数,也就是说时间复杂度为O(n),其实也比较优秀了,但是还有更好的方法
因为是升序,我们类似二分查找的思想,我们用来查找中位数,每次舍弃不符合条件的部分
所以关键问题是判断出不符合条件的的部分
首先我们要明确
两个数列合并后,长度为偶数2L,我们要求的中位数,其实就是第L个数字
也就是前一半序列的最后一个数字,
也就是说合并后一定有L-1个数字小于等于中位数,一定有L个数字大于等于中位数,这是必须符合的条件
我们先分别找到这两个序列的中位数,记作a,b
但是注意,两个序列长度都是L,L的奇偶性所得到的中位数是不一样的
然后我们要分两种情况讨论
(1)L为奇数的时候
因为是升序序列,所以a,b前边的数都比他们小
因为L是奇数,所以很容易算出a,b前边共有(L-1)个数字,
- 若a=b 那说明前边的(L-1)个数都小于a或b,a,b必然是中位数
- 若a<b 那么对于a前边的(L-1)/2个数来说,他们都不可能是中位数
因为a,b后边的(L-1)个数再加上a,b本身。一共L+1个数字,都比他们大
同理,b后边的(L-1)/2个数字也不可能有中位数,舍弃这两部分 - 若a>b 和a<b是一样。
舍弃之后,你可以思考一下,我们把不可能是中位数的数舍弃了,其实这个操作相等于对于合并后的序列,掐头去尾
删除了前边和后边不可能是中位数的数字
那么剩下的,我们继续进行这样的操作,再掐头去尾,直到最后,一定会两个序列都只剩下一个数字,那么这两个数
较小的那个就是中位数(为什么是较小的呢?因为合并后长度是偶数,根据题意偶数的中位数偏前边~
注意一点:这个原理说明了,我们每次删除的前后必须长度一致
还需要注意的是:我们舍弃的时候,要不要连a,b一起舍弃呢?
这取决于a,b有没有可能是我们要求的中位数
上面我们说过,a<b时,a前边的数都不可能是,那么a有没有可能呢
显然,是有可能的,a前边(L-1)/2个数都小于a,若b前边的(L-1)/2个数也小于a,
那么a就是中位数了,且未破坏a<b的条件,所以a不能删除
但是b不可能是中位数,因为已知就有L个数小于b了,但是呢b也不能删,因为上边讲原理的时候说了你必须满足每次掐头去尾的长度一致,这样才能在留下的序列中继续找中位数
所以结论是:L为奇数是,舍弃时保留a,b
(2)L为偶数
L是偶数的话,很容易算出a,b前边共有(L-2)个数字,
a=b就不说了
a<b时,a不可能是中位数,因为已经存在(L+1)个数大于a(a后边的数+b后边的数+b本身)
所以a可以删除,b是有可能是中位数的,b不能删除,而且
偶数时你要保证掐头去尾的长度一致,就必须a,b留一个删一个,才能相等
故结论是: L为偶数时,舍弃时保留大的,删除小的
a>b 同理
答案:
设计思路:分别求出两个升序序列A、B的中位数a、b
1、若a=b 则a,b即为所求的中位数
2、若a<b 则舍弃A中较小的一半,同时舍去B中较大的一半,要求两次舍弃的长度相等
3、若a>b 则舍弃B中较小的一半,同时舍去A中较大的一半,要求两次舍弃的长度相等
在保留下来的两个升序序列中,重复上述三个步骤,直到两个序列中都只上一个元素为止,
较小的就是所求的中位数
算法描述:
int M_search(int A[],int B[],int n){
int s1=0,d1=n-1,s2=0,d2=n-1,m1,m2; //起点 终点 中间点
while(s1!=d1||s2!=d2){
m1=(s1+d2)/2;
m2=(s2+d2)/2;
if(A[m1]==B[m2]) return A[m1]; //若相等
if(A[m1]<B[m2]){ //若a<b
if((s1+d1)%2==0){ //若长度是奇数
s1=m1; //删除a前边的并保留a
d1=m2; //删除b后边的并保留b
}
else{ //若长度是偶数
s1=m1+1; //删除a以及a前边的
d2=m2; //删除b后边的并保留b
}
}
else{ //a>b 与上边同理 互换操作即可
if((s2+d2)%2==0){
d1=m1;
s2=m2;
}
else{
d1=m1;
s2=m2+1;
}
}
}
return A[s1]<B[s2]?A[s1]:B[S2];
}
复杂度分析:
每次舍弃的长度是总长度的一半,所以跟二分查找一样
时间复杂度O(logn) 空间复杂度O(1)
###############################################