中位数选取及第k位数选取

中位数选取

描述

任给有序向量S1和S2,如何找出它们归并后所得有序向量S=S1∪S2的中位数?

蛮力解法

代码实现(C++)
template <typename T>
T trivialMedian(vector<T>& S1, int lo1, n1, vector<T>& S2, int lo2, int n2) {
    int hi1 = lo1 + n1, hi2 = lo2 + n2;
    vector<T> S;
    while((lo1 < hi1) && (lo2 < hi2)) {
        while((lo1<hi1) && S1[lo1] <= S2[lo2])
            S.push_back(S1[lo1++]);
        while((lo2<hi2) && S2[lo2] <= S1[lo1])
            S.push_back(S2[lo2++]);
    }
    while(lo1 < hi1) S.push_back(S1[lo1++]);
    while(lo2 < hi2) S.push_back(S2[lo2++]);
    return S[(n1+n2)/2];
}
复杂度分析

时间复杂度、空间复杂度均为O(n1 + n2)。

优化

  以上蛮力解法没有充分利用“两个子向量已经有序”的条件,仅适合n1、n2不太大的情况下。实际上可以充分利用“已经有序”的条件,以及“减而治之”的思想,来更快地完成这一任务。

代码实现
template <typename T>
T median(vector<T>& S1, int lo1, int n1, vector<int>& S2, int lo2, int n2) {
    if(n2 < n1)
        return median(S2, lo2, n2, S1, lo1, n1);         //严格保证n1<=n2
    if(n2 < 6)
        return trivialMedian(S1, lo1, n1, S2, lo2, n2);  //递归基
    if(2*n1 < n2)//若两个向量长度相差悬殊,则长者(S2)的两翼可直接截除
        return median(S1, lo1, n1, S2, lo2 + (n2 - n1 - 1) / 2, n1 + 2 - (n2 - n1) % 2);

    int mi1 = lo1 + n1/2;
    int mi2a = lo2 + n2/2;
    int mi2b = lo2 + n2 - 1- n1/2;
    if(S2[mi2b] < S1[mi1])                //取S1左半、S2右半
        return median(S1, lo1, n1/2+1, S2, mi2a, n2-(n1-1)/2);
    else if(S1[mi1] < S1[mi2a])           //取S1右半、S2左半
        return median(S1, mi1, (n1+1)/2, S2, lo2, n2-n1/2);
    else                                  //S1保留,S2左右同时缩短
        return median(S1, lo1, n1, S2, mi2a, n2-(n1-1)/2*2);
}
复杂度分析

时间复杂度为O(log(n1+n2)),更精确地,其复杂度为O(log(min(n1,n2)))。

第k位数选取

  第k位数选取是中位数选取的更加一般的问题。

代码实现

template <typename T> void quickSelect(vector<T>& A, Rank k) {
    for(int lo = 0, hi = A.size() - 1; lo < hi; ) {
        int i = lo, j = hi;
        T pivot = A[lo];
        while(i < j) {
            while((i<j) && (pivot <= A[j])) 
                --j;
            A[i] = A[j];
            while((i<j) && (A[i] <= A[i]))
                ++i;
            A[j] = A[i];
        }// assert: i == j
        A[i] = pivot;
        if(k <= i) hi = i - 1;
        if(i <= k) lo = i + 1;
    }//A[k] is now a pivot
}
复杂度分析

最坏情况下时间复杂度为O(n2)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值