codility上的问题 (14)Nu 2011

这个题描述不麻烦,给定两个有序的整数数组A,B长度分别为M,N,再给定4个长度为K的数组P,Q,R,S。0<=P[i]<=Q[i]< M, 0<=R[i]<=S[i]< N

把A中子数组[P[i],Q[i]]截取出来,B中子数组[R[i],S[i]]截取出来,形成一个数组的中位数一共有K个,求这K个数的中位数。

数据范围N,M [1..10^5]

K [1..10^4]

要求复杂度  时间 O(K*(log(N)+log(M))), 空间 O(k)

为了方便 保证K是奇数,以及Q[i] - P[i] + 1 +S[i] - Q[i] + 1 都是奇数。


分析:

这个题对k个子数组,分别求中位数即可,这就用到了两个有序数组的第k元素的算法。它可以在log(M) + log(N)时间解决。

具体算法: 

假设a数组有numa个元素,b数组有umb个元素。如果有一个是空的,则直接返回另外一个一个数组的对应元素。

如果k = 1或者k = numa + numb,分别返回最小和最大的元素。

否则把k分为ka, kb两个部分,ka + kb = k

我们假想一个归并过程,

考虑如果a[ka - 1] <= b[kb - 1],则我们先归并 a[ka - 1],再归并b[kb - 1]。归并完a[ka - 1]之后,归并的总个数小于ka + kb = k,所以a[0..ka - 1] 种不包含第k小的元素。同理,再归并完b[kb - 1]之后,我们归并的元素数一定大于等于ka + kb = k,所以b[kb..N - 1]一定也不包含第k元素。所以a的前半段和b的后半段可以扔掉。a[ka - 1] > b[kb - 1]时,情况对称。

总之,我们每次可以扔掉较小那个元素的数组前面的部分(含本身),和较大那个元素的数组的后面的部分(不含本身)。

这个问题解决之后,我们得到一个长度为C的数组,可以直接排序求中位数,也可以用类似快拍partition的方法期望线性时间求其中位数。

貌似快排的话最后总复杂度是O(k * (log(M) + log(N) + log(K))),不快排应该是O(K * (log(M) + log(N)))。

代码:

 

int give(int *a,int *b,int numa,int numb,int k) {
    if (numa == 0) {
        return b[k - 1];
    }
    if (numb == 0) {
        return a[k - 1];
    }
    if (k == 1) {
        return (a[0] < b[0])?a[0]:b[0];
    }
    if (k == numa + numb) {
        return (a[numa - 1] > b[numb - 1])?a[numa - 1]:b[numb - 1];
    }
    int ka = k * 1. * numa / (numa + numb);
    if (ka == 0) {
        ka = 1;
    }
    int kb = k - ka;
    if (kb > numb) {
        kb = numb;
        ka = k - kb;
    }
    return (a[ka - 1] <= b[kb - 1])?give(a + ka, b, numa - ka, kb, k - ka):give(a, b + kb, ka, numb - kb, k - kb);
}
        

int findk(int *a,int n,int k) {
int i,j,t;
 
    for (i = 1, j = n - 1;; ++i,--j) {
        for (; (i <= j) && (a[i] < a[0]); ++i)   //i <= j 这里可以写(i < n)
        ;
        for (; (j >= i) && (a[j] > a[0]); --j)    // j >= i 这里可以写(j > 0)
        ;
            
        if (i >= j) {
            break;
        }
      	t = a[i];
        a[i] = a[j];
        a[j] = t;
       
    }
    t = a[0];
    a[0] = a[j];
    a[j] = t;
    if (j == k - 1) {
        return a[j];
    }
    if (j > k - 1) {
        return findk(a, j , k);
    }
    else {
        return findk(a + j + 1, n - j - 1, k - j - 1);
    }
    
}
int c[10002];
    
int solution(int A[], int N, int B[], int M, int P[], int Q[], int R[], int S[], int K) {
    // write your code here...
int i;
    for (i = 0; i < K; ++i) {
        c[i] = give(A + P[i], B + R[i], Q[i] - P[i] + 1, S[i] - R[i] + 1, (Q[i] - P[i] + 1 + S[i] - R[i] + 1) / 2 + 1);
    }
    return findk(c, K , K / 2 + 1);
 
}


你可能感兴趣的:(codility)