算法第二章上机实践报告

7-3 两个有序序列的中位数 (20 分)

已知有两个等长的非降序序列S1, S2, 设计函数求S1与S2并集的中位数。有序序列,的中位数指A(N1)/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;
} 
View Code

 

算法分析
  时间复杂度:数组长度为n,所以二分的时间复杂度是O(logn),输入数据的时间复杂度是O(n),所以算法时间复杂度是O(logn)(强行口胡)
  空间复杂度:只开了4个变量,显然O(1)
        
心得体会
  写二分一定要思路清晰,不然很容易出问题,要知道l,r该怎么移动,为什么这样移动。

posted @ 2019-09-20 14:36  丿不落良辰  阅读(174)  评论(0编辑  收藏  举报