题目
一个长度为
的升序序列S
,处在第[L/2]
个位置的数称为S
的中位数。例如,若序列
,则S_1的中位数是15,两个序列的中位数是含他们所有元素的升序序列的中位数,例如,若
,则
和
的中位数是11,现在需要找出序列
和
的中位数。
思路:
分别求两个升序序列 的中位数,设为 和 ,求序列 的中位数过程如下:
- 若 ,则 或 即为所求中位数,算法结束,
- 若 ,则舍弃序列 中较小的一半,同时舍弃序列 中较大的一半,要求两次舍弃的长度相等,
- 若
,则舍弃序列
中较大的一半,同时舍弃序列
中较小的一半,要求两次舍弃的长度相等,
在保留的两个升序序列中,重复过程 ,直到两个序列中均只含一个元素为止,较小者即为所求的中位数。
思路依据:
- 的情形,若将 合并到 中,则 的前半部分放到 的前半部分, 的后半部分放到 的后半部分,所以中位数不改变,
- 的情形,若将 合并到 中,则至少 及前半部分放到 的前半部分,此时最好的情况 就是中位数,那么 的前半部分就不可能包含中位数,显然 的后半部分也不可能包含中位数,所以在两边同时删去一半,对最终结果没有影响。
- 的情形同 一样。
代码实现:
#include<iostream>
#include<stdio.h>
using namespace std;
int M_search(int A[], int B[], int n){
int s1=0, d1=n-1, m1;
int s2=0, d2=n-1, m2;
while(s1 != d1 || s2 != d2){
m1 = (s1+d1)/2;
m2 = (s2+d2)/2;
if(A[m1] == B[m2])
return A[m1];
if(A[m1] < B[m2]){
if((s1+d1)%2 == 0){ //元素个数为奇数
s1 = m1; //舍弃A中间点以前的部分且保留中间点
d2 = m2; //舍弃B中间点以后的部分且保留中间点
}
else{ //元素个数为偶数
s1 = m1+1; //舍弃A中间点以及中间点以前的部分
d2 = m2; //舍弃B中间点以后的部分且保留中间点
}
}
else{
if((s2+d2)%2 == 0){
s2=m2;
d1=m1;
}
else{
s2=m2+1;
d1=m1;
}
}
}
return A[s1] < B[s2] ? A[s1]:B[s2];
}
int main(){
int A[5] = {11, 13, 15, 17, 19};
int B[5] = {2, 4, 6, 8, 20};
printf("%d\n", M_search(A, B, 5));
}
分析:
- 时间复杂度为 ,空间复杂度为 。
- 当然本题可以采用先合并 、 ,在求中位数的办法,但是复杂度为 ,不是最优算法。