从两个排序的数组中找到kthsmallest元素

时间:2014-02-25 04:20:56

标签: arrays algorithm sorting

我已经实现了一个可以完成它的工作函数。但它效率不高,因为它会在每次调用中复制一个新副本。我无法将其转换为仅使用a_start,a_end,b_start,b_end。我已经尝试了几种方法来转换它,但它们都没有适用于所有情况。我如何转换它,以便它接收数组a和b的开始和结束指针? 我尝试了以下内容并修改了k-i-1和k-j-1,这样它只需要k,但是没有用。

int m = a_right-a_left, n=b_right-b_left;
int i = (a_left+a_right)/2;
or int i = (int)((m* (k-1)) / (m+n) ); 

下面是我的工作代码,每次调用使用一个新的数组副本。

public static int kthSmallest(int[] a, int[] b,  int k) {
    if (a.length==0)
        return b[k-1];
    else if (b.length==0)
        return a[k-1];
    else if (b.length<a.length)
        return kthSmallest(b, a, k);

        // make sure i + j = k - 1
        int m = a.length, n=b.length;
        int i = (int)((double)m / (m+n) * (k-1));  // make sure i won't be out of bounds
        int j = k - 1 - i;
        int bj_1 = 0, ai_1 = 0;

        if (i==0) {  ai_1 = Integer.MIN_VALUE; } // in case i = 0, outOfBound
        else {  ai_1 = a[i-1]; }
        if (j==0) { bj_1 = Integer.MIN_VALUE; } // in case j = 0, outOfBound
        else {  bj_1 = b[j-1]; }

        if (bj_1 < a[i] && a[i] < b[j]) // kth smallest found, b[j-1] < a[i] < b[j]
            return a[i];
        if (ai_1 < b[j] && b[j] < a[i]) // kth smallest found, a[i-1] < b[j] < a[i]
            return b[j];

        if ( a[i] < b[j] ) // if true, exclude a's lower bound (if 2 arrays merged, a's lower bound must
            // reside before kth smallest, so also update k. 
            // also exclude b's upper bound, since they are all greater than kth element.
            return kthSmallest(Arrays.copyOfRange(a, i+1, a.length), Arrays.copyOfRange(b, 0, j), k-i-1);
        else
            return kthSmallest(Arrays.copyOfRange(a, 0, i), Arrays.copyOfRange(b, j+1, b.length), k-j-1);
}

5 个答案:

答案 0 :(得分:9)

这是来自the answer to "How to find the kth smallest element in the union of two sorted arrays?" questionO(log a.length + log b.length)算法。它是从C++ recursive implementation到Java的直接端口:

public static int ksmallest(int[] a, int[] b,
                            int a1, int a2, int b1, int b2,
                            int k) {
    int lena = a2 - a1;
    int lenb = b2 - b1;
    assert (0 <= k && k < (lena + lenb));
    if (lena == 0) {
        return b[b1 + k];
    }
    if (lenb == 0) {
        return a[a1 + k];
    }
    int mida = lena / 2;
    int midb = lenb / 2;
    int ma = a[a1 + mida];
    int mb = b[b1 + midb];
    if ((mida + midb) < k) {
        return (mb < ma) ?
            ksmallest(a, b, a1, a2, b1 + midb + 1, b2, k - (midb + 1)) :
            ksmallest(a, b, a1 + mida + 1, a2, b1, b2, k - (mida + 1));
    }
    else {
        return (mb < ma) ?
            ksmallest(a, b, a1, a1 + mida, b1, b2, k) :
            ksmallest(a, b, a1, a2, b1, b1 + midb, k);
    }
}

还有C++ iterative implementation具有相同的时间复杂度(没有递归)。它可以像递归版本一样移植到Java。

验证递归版本:

/** concatenate a, b arrays */
public static int[] concatenate(int[] a, int[] b) {
    int lena = a.length;
    int lenb = b.length;
    int[] c = new int[lena + lenb];
    System.arraycopy(a, 0, c, 0, lena);
    System.arraycopy(b, 0, c, lena, lenb);
    return c;
}

public static void main(String[] args) {
    int a[] = {0, 3, 7, 8};
    int b[] = {0, 2, 3};
    int c[] = concatenate(a, b);
    Arrays.sort(c);
    for (int n = 0; n < (a.length + b.length); n++) {
        int k = ksmallest(a, b, 0, a.length, 0, b.length, n);
        if (k != c[n]) {
            System.out.println(n + ": expected " + c[n] + " got " + k);
        }
    }
}

成功时,它什么都不打印。

答案 1 :(得分:1)

具有O(n)但易于理解的算法;

//both arrays are sorted
private int getKthSmallestElement(int[] array1, int[] array2, int k) {
    int elem=-1;
    int index1=0,index2=0;
    while(k != 0 && (index1<array1.length) && (index2 < array2.length))
    {            
        if(array1[index1] < array2[index2])
        {
            index1++;
        }
        else
            index2++;
        k--;         
    }

    if((index1<array1.length) && (index2 < array2.length))
        return array1[index1] > array2[index2] ? array2[index2] :array1[index1] ;
    else
    {
        if(index1 >= array1.length)
        {
            return array2[index2+k]; 
        }
        else{
            return array1[index1+k];            
        }
    }
}

答案 2 :(得分:0)

这是一个O((logn)^2)简单的递归解决方案,无需复制: -

public class KthElement {
    public static int binSearch(int[] arr,int low,int high,int key) {
        int mid=0;
        while(high>=low) {
            mid = (high+low)/2;
            if(arr[mid]==key) {
                return(mid);
            }
            else if(arr[mid]<key) {
                low = mid+1;
            }
            else {
                high = mid-1;
            }
        }
        if(arr[mid]>key) {
            return(mid-1);
        }
        return(mid);
    }

    public static int kthElement(int[] arr1,int[] arr2,int s1,int h1,int s2,int h2,int k) {
        int len1 = (h1-s1+1);
        int len2 = (h2-s2+1);
        if(len1<=0) {
            return(arr2[s2+k-1]);
        }
        if(len2<=0) {
            return(arr1[s1+k-1]);
        }

        if(k>(len1+len2)) {
            return(-1);
        }

        int mid = (s1+h1)/2;
        int i = binSearch(arr2,s2,h2,arr1[mid]);
        int size = mid+i-s1-s2+2;
        //System.out.println(mid+" "+i+" "+size);
        if(size==k){
            return(arr1[mid]);
        }
        if(size>k) {
            return(kthElement(arr1, arr2, s1,mid-1, s2,i, k));
        }
        else {
            return(kthElement(arr1, arr2, mid+1,h1,i+1,h2, k-size));
        }

    } 

    public static void main(String[] args) {
        int[] arr1 = {1,3,5,7,9,11,13};
        int[] arr2 = {2,4,6,8,10,12};
        int k = 6;
        System.out.println(k+"th Element : "+kthElement(arr1, arr2,0,arr1.length-1, 0,arr2.length-1, k));
    }

}

答案 3 :(得分:0)

这是JAVA迭代解决方案:

  

O(log A.length + log B.length)时间复杂度

public static int kthsmallest(int []A, int []B, int K){

    int begin = Math.max(0,K-B.length); // binary search begin index
    int end   = Math.min(A.length,K);  // binary search end end index

    while(begin < end){
        // search until mid = k
        int mid = begin +(end-begin)/2;

        if(mid<A.length && K-mid>0 && A[mid]<B[K-mid-1]){
            begin = mid+1;

        }else if( mid > 0 && K-mid <B.length && A[mid-1]>B[K-mid]){
            end = mid;

        }else{
            begin=mid;
            break;
        }
    }

    if(begin ==0){
        return B[K-1];
    }else if(begin == K){
        return A[K-1];
    }else{
        return Math.max(A[begin -1],B[K-begin-1]);
    }
}

答案 4 :(得分:0)

与问题中提到的逻辑相同,但稍微改变了一点,不为每次调用创建新数组。

//alow, ahigh are used to maintain the size of a array we are using instead 
//of creating a new array. imilarly for blow, bhigh
public static int search(int[] a, int[] b, int alow, int ahigh, int blow, int bhigh, int k){

    if (alow>ahigh) // if "a" array is of zero length, then take kth element from "b"
        return b[blow+k-1];
    else if (blow>bhigh || bhigh-blow == 0) // Similarly, // if "b" array is of zero length, then take kth element from "a"
        return a[alow+k-1];
    else if ((bhigh-blow)<(ahigh-alow)) //always make sure that a's length is lower length
        return search(b, a,blow,bhigh,alow,ahigh, k);

        // make sure i + j = k - 1
        int m = ahigh-alow+1, n=bhigh-blow+1;
        int i = (int)((double)m / (m+n) * (k-1));  // make sure i won't be out of bounds
        int j = Math.min(k - 1 - i,n-1);
        int bj_1 = 0, ai_1 = 0;

        if (i==0) {  ai_1 = Integer.MIN_VALUE; } // in case i = 0, outOfBound
        else {  ai_1 = a[i-1]; }
        if (j==0) { bj_1 = Integer.MIN_VALUE; } // in case j = 0, outOfBound
        else {  bj_1 = b[j-1]; }

        if (bj_1 < a[i] && a[i] < b[j]) // kth smallest found, b[j-1] < a[i] < b[j]
            return a[i];
        if (ai_1 < b[j] && b[j] < a[i]) // kth smallest found, a[i-1] < b[j] < a[i]
            return b[j];

        if ( a[i] < b[j] ) // if true, exclude a's lower bound (if 2 arrays merged, a's lower bound must
            // reside before kth smallest, so also update k. 
            // also exclude b's upper bound, since they are all greater than kth element.
            return search(a, b,alow+i+1,ahigh,   blow,j-1,         k-i-1);
        else
            return search(a,b,alow,  i-1,        blow+j+1,bhigh,   k-j-1);
    }