算法第二章上机实践报告
7-3 两个有序序列的中位数 (20 分)
已知有两个等长的非降序序列S1, S2, 设计函数求S1与S2并集的中位数。有序序列,的中位数指A(N−1)/2的值,即第⌊(N+1)/2⌋个数(A0为第1个数)。
输入格式:
输入分三行。第一行给出序列的公共长度N(0<N≤100000),随后每行输入一个序列的信息,即N个非降序排列的整数。数字用空格间隔。
输出格式:
在一行中输出两个输入序列的并集序列的中位数。
输入样例1:
5
1 3 5 7 9
2 3 4 5 6
输出样例1:
4
输入样例2:
6
-100 -10 1 1 1 1
-50 0 2 3 4 5
输出样例2:
1
题意:把两个数组按非递减的顺序合起来后求中位数。
解题思路(算法描述):因为两个数组本身有序,可对两个数组进行二分。
我们不断维护两个数组的二分区间la,ra和lb,rb,保证两个区间中元素个数相同
定义:
第一个数组名为a, ai表示数组第i个元素, 第二个数组名为b, bi表示数组第i个元素
ma=(la+ra)/2, mb=(lb+rb)/2
根据定义,可以知道当l到r之间的元素为奇数个时ma + mb = n + 1
当l到r之间的元素为偶数个时ma + mb = n
1.二分的时候遇到a[ma] == b[mb]的时候
若l到r元素个数为偶数 ma + mb = n
我们把a[1]到a[ma],b[1]到b[mb]的元素拼起来,共n个元素,其中第n个元素就是问题答案
因为原数组有序,所以a[ma]和b[mb]肯定是该数组最大的数,而a[ma+1]和b[mb+1]都是大于等于新数组的所以元素,故无需考虑ma,mb之后的数了
若l到r元素个数为奇数 ma + mb = n + 1
同上,a[ma]和b[mb]必为答案
2.二分的时候遇到a[ma] > b[mb]的时候
因为a[ma+1] >= a[ma] > b[mb]
比a[ma+1]小的元素肯定大于等于n个
所以数组a的区间ma+1到r的元素不用考虑了
所以ra = ma,la = mb,调整二分区间,然后继续二分,b[mb] > a[ma]同理
注意让两个区间的个数相等
参考代码:
#include <iostream> #include <math.h> using namespace std; int a[100010],b[100010],n; int find(int la,int ra,int lb,int rb) { if (la == ra) { if (a[la] == b[lb]) return a[la]; else return a[la] > b[lb] ? b[lb] : a[la]; } int ma = la + ra >> 1; int mb = lb + rb >> 1; if (a[ma] == b[mb]) return a[ma]; else if (a[ma] > b[mb]) { ra = ma; lb = mb; if(ra - la != rb - lb) lb++; return find(la,ra,lb,rb); } else { rb = mb; la = ma; if(ra - la != rb - lb) la++; return find(la,ra,lb,rb); } } int main() { ios::sync_with_stdio(0); cin >> n; for(int i=1; i<=n; ++i) cin >> a[i]; for(int i=1; i<=n; ++i) cin >> b[i]; a[0] = b[0] = -199999999; cout << find(1,n,1,n); return 0; }
算法分析:
时间复杂度:数组长度为n,所以二分的时间复杂度是O(logn),输入数据的时间复杂度是O(n),所以算法时间复杂度是O(logn)(强行口胡)
空间复杂度:只开了4个变量,显然O(1)
心得体会:
写二分一定要思路清晰,不然很容易出问题,要知道l,r该怎么移动,为什么这样移动。