Làm thế nào để tìm phần tử nhỏ nhất thứ k trong hợp của hai mảng đã sắp xếp?


106

Đây là một câu hỏi bài tập về nhà. Họ nói rằng nó chiếm O(logN + logM)vị trí NMđộ dài của mảng.

Hãy đặt tên cho các mảng ab. Rõ ràng là chúng ta có thể bỏ qua tất cả a[i]b[i]ở đâu i> k.
Đầu tiên chúng ta hãy so sánh a[k/2]b[k/2]. Để b[k/2]> a[k/2]. Do đó, chúng ta cũng có thể loại bỏ tất cả b[i], trong đó i> k / 2.

Bây giờ chúng ta có tất cả a[i], nơi tôi <k và tất cả b[i], nơi tôi <k / 2 để tìm câu trả lời.

Bước tiếp theo là gì?


6
Tất cả các bước này có được đưa vào bài tập không hay các bước trên là bước khởi đầu cho thuật toán của bạn?
Kendrick

18
Các bước trên là của tôi.
Michael

Có phải O(logN + logM)chỉ đề cập đến thời gian cần thiết để tìm phần tử thứ k? Có thể thực hiện tiền xử lý trước với union không?
David Weiser

1
@David. Không cần xử lý trước.
Michael

3
Có được phép trùng lặp trong các mảng không?
David Weiser

Câu trả lời:


48

Bạn đã có nó, chỉ cần tiếp tục! Và hãy cẩn thận với các chỉ mục ...

Để đơn giản hóa một chút, tôi sẽ giả sử rằng N và M> k, vì vậy độ phức tạp ở đây là O (log k), là O (log N + log M).

Mã giả:

i = k/2
j = k - i
step = k/4
while step > 0
    if a[i-1] > b[j-1]
        i -= step
        j += step
    else
        i += step
        j -= step
    step /= 2

if a[i-1] > b[j-1]
    return a[i-1]
else
    return b[j-1]

Để trình diễn, bạn có thể sử dụng bất biến vòng lặp i + j = k, nhưng tôi sẽ không làm tất cả bài tập về nhà của bạn :)


14
Đây không phải là một bằng chứng thực tế, nhưng ý tưởng đằng sau thuật toán là chúng ta duy trì i + j = k, và tìm i và j sao cho a [i-1] <b [j-1] <a [i] ( Hoặc đường vòng khác). Bây giờ vì có i phần tử trong 'a' nhỏ hơn b [j-1] và j-1 phần tử trong 'b' nhỏ hơn b [j-1], b [j-1] là i + j-1 + 1 = phần tử nhỏ nhất thứ k. Để tìm i, j như vậy, thuật toán thực hiện tìm kiếm lưỡng phân trên các mảng. Có ý nghĩa?
Jules Olléon

8
Sao cho O (log k) là O (log n + log m)?
Rajendra Uppal

7
Điều này không có tác dụng nếu tất cả các giá trị trong mảng 1 đến trước các giá trị trong mảng 2.
John Kurlak

3
Tại sao bạn sử dụng k / 4 như một bước đầu tiên?
Maggie

2
Như @JohnKurlak đã đề cập, nó không hoạt động đối với các giá trị trong đó toàn bộ a nhỏ hơn b. Hãy xem repl.it/HMYf/0
Jeremy S.

69

Tôi hy vọng tôi không trả lời bài tập về nhà của bạn vì đã hơn một năm kể từ khi câu hỏi này được hỏi. Đây là một giải pháp đệ quy đuôi sẽ mất thời gian log (len (a) + len (b)).

Giả định: Các đầu vào là đúng. tức là k nằm trong khoảng [0, len (a) + len (b)]

Các trường hợp cơ bản:

  • Nếu độ dài của một trong các mảng là 0, thì câu trả lời là phần tử thứ k của mảng thứ hai.

Các bước giảm:

  • Nếu chỉ số giữa của a+ chỉ số giữa của bnhỏ hơnk
    • Nếu phần tử giữa của alớn hơn phần tử giữa của b, chúng ta có thể bỏ qua nửa đầu của b, điều chỉnh k.
    • khác bỏ qua nửa đầu của a, điều chỉnh k.
  • Khác nếu knhỏ hơn tổng các chỉ số giữa ab:
    • Nếu phần tử giữa của alớn hơn phần tử giữa của b, chúng ta có thể an toàn bỏ qua nửa sau củaa
    • nếu không, chúng ta có thể bỏ qua nửa sau của b

Mã:

def kthlargest(arr1, arr2, k):
    if len(arr1) == 0:
        return arr2[k]
    elif len(arr2) == 0:
        return arr1[k]

    mida1 = len(arr1)/2
    mida2 = len(arr2)/2
    if mida1+mida2<k:
        if arr1[mida1]>arr2[mida2]:
            return kthlargest(arr1, arr2[mida2+1:], k-mida2-1)
        else:
            return kthlargest(arr1[mida1+1:], arr2, k-mida1-1)
    else:
        if arr1[mida1]>arr2[mida2]:
            return kthlargest(arr1[:mida1], arr2, k)
        else:
            return kthlargest(arr1, arr2[:mida2], k)

Xin lưu ý rằng giải pháp của tôi là tạo bản sao mới của các mảng nhỏ hơn trong mỗi lần gọi, điều này có thể dễ dàng loại bỏ bằng cách chỉ chuyển các chỉ số đầu và cuối trên các mảng ban đầu.


4
tại sao bạn lại gọi nó kthlargest()(k+1)phần tử nhỏ nhất -th, ví dụ, 1là phần tử nhỏ thứ hai 0,1,2,3, tức là hàm của bạn trả về sorted(a+b)[k].
jfs 27/07/12


1
bạn có thể vui lòng giải thích tại sao việc so sánh tổng các chỉ số giữa của a và b với k lại quan trọng không?
Maggie

3
Trong các bước rút gọn, điều quan trọng là phải loại bỏ một số phần tử trong một trong các mảng tỷ lệ với độ dài của nó để làm cho logarit thời gian chạy. (Ở đây chúng tôi đang loại bỏ một nửa). Để làm được điều đó, chúng ta cần chọn một mảng có một trong các nửa mà chúng ta có thể bỏ qua một cách an toàn. làm sao chúng ta làm việc đó bây giờ? Bằng cách tự tin loại bỏ một nửa mà chúng ta biết chắc chắn rằng sẽ không có phần tử thứ k.
lambdapilgrim

1
So sánh k với tổng các nửa độ dài của mảng cho chúng ta thông tin về một nửa của một trong các mảng có thể bị loại bỏ. Nếu k lớn hơn tổng của nửa độ dài, chúng ta biết rằng nửa đầu của một trong các mảng có thể bị loại bỏ. Ngược lại nếu k nhỏ hơn. Lưu ý rằng chúng ta không thể loại bỏ một nửa khỏi mỗi mảng cùng một lúc. Để quyết định nửa mảng nào cần loại bỏ, chúng ta tận dụng lợi thế của thực tế là cả hai mảng đều được sắp xếp, vì vậy nếu k lớn hơn tổng các nửa độ dài, chúng ta có thể loại bỏ nửa đầu của mảng có phần tử ở giữa nhỏ hơn hai yếu tố giữa. Ngược lại.
lambdapilgrim

34

Nhiều người đã trả lời câu hỏi "phần tử nhỏ nhất thứ k từ hai mảng được sắp xếp" này, nhưng thường chỉ với những ý tưởng chung chung, không phải là một mã làm việc rõ ràng hoặc phân tích điều kiện biên.

Ở đây tôi muốn giải thích kỹ lưỡng theo cách tôi đã làm để giúp một số người mới hiểu, với mã Java hoạt động chính xác của tôi. A1A2hai mảng tăng dần sắp xếp, với size1size2như chiều dài tương ứng. Chúng ta cần tìm phần tử nhỏ nhất thứ k từ hợp của hai mảng đó. Ở đây, chúng tôi giả định một cách hợp lý rằng (k > 0 && k <= size1 + size2), điều đó ngụ ý rằng A1A2 không thể để trống.

Đầu tiên, hãy tiếp cận câu hỏi này với thuật toán O (k) chậm. Phương pháp này là so sánh phần tử đầu tiên của cả mảng A1[0]A2[0]. Lấy cái nhỏ hơn, A1[0]bỏ vào túi của chúng tôi. Sau đó so sánh A1[1]với A2[0], v.v. Lặp lại hành động này cho đến khi túi của chúng tôi đạt đến kcác phần tử. Rất quan trọng: Trong bước đầu tiên, chúng tôi chỉ có thể cam kết A1[0]trong túi của mình. Chúng tôi KHÔNG thể bao gồm hoặc loại trừ A2[0]!!!

Mã O (k) sau đây cho bạn một phần tử trước câu trả lời đúng. Ở đây tôi sử dụng nó để hiển thị ý tưởng của tôi và điều kiện ranh giới phân tích. Tôi có mã chính xác sau mã này:

private E kthSmallestSlowWithFault(int k) {
    int size1 = A1.length, size2 = A2.length;

    int index1 = 0, index2 = 0;
    // base case, k == 1
    if (k == 1) {
        if (size1 == 0) {
            return A2[index2];
        } else if (size2 == 0) {
            return A1[index1];
        } else if (A1[index1].compareTo(A2[index2]) < 0) {
            return A1[index1];
        } else {
            return A2[index2];
        }
    }

    /* in the next loop, we always assume there is one next element to compare with, so we can
     * commit to the smaller one. What if the last element is the kth one?
     */
    if (k == size1 + size2) {
        if (size1 == 0) {
            return A2[size2 - 1];
        } else if (size2 == 0) {
            return A1[size1 - 1];
        } else if (A1[size1 - 1].compareTo(A2[size2 - 1]) < 0) {
            return A1[size1 - 1];
        } else {
            return A2[size2 - 1];
        }
    }

    /*
     * only when k > 1, below loop will execute. In each loop, we commit to one element, till we
     * reach (index1 + index2 == k - 1) case. But the answer is not correct, always one element
     * ahead, because we didn't merge base case function into this loop yet.
     */
    int lastElementFromArray = 0;
    while (index1 + index2 < k - 1) {
        if (A1[index1].compareTo(A2[index2]) < 0) {
            index1++;
            lastElementFromArray = 1;
            // commit to one element from array A1, but that element is at (index1 - 1)!!!
        } else {
            index2++;
            lastElementFromArray = 2;
        }
    }
    if (lastElementFromArray == 1) {
        return A1[index1 - 1];
    } else {
        return A2[index2 - 1];
    }
}

Ý tưởng mạnh mẽ nhất là trong mỗi vòng lặp, chúng tôi luôn sử dụng cách tiếp cận trường hợp cơ sở. Sau khi cam kết với phần tử nhỏ nhất hiện tại, chúng tôi tiến gần hơn một bước tới mục tiêu: phần tử nhỏ nhất thứ k. Đừng bao giờ nhảy vào giữa và khiến bản thân bối rối và lạc lối!

Bằng cách quan sát trường hợp cơ sở mã ở trên k == 1, k == size1+size2, và kết hợp với nó A1A2không được để trống cả hai. Chúng ta có thể chuyển logic thành dưới đây theo phong cách ngắn gọn hơn.

Đây là một mã làm việc chậm nhưng chính xác:

private E kthSmallestSlow(int k) {
    // System.out.println("this is an O(k) speed algorithm, very concise");
    int size1 = A1.length, size2 = A2.length;

    int index1 = 0, index2 = 0;
    while (index1 + index2 < k - 1) {
        if (size1 > index1 && (size2 <= index2 || A1[index1].compareTo(A2[index2]) < 0)) {
            index1++; // here we commit to original index1 element, not the increment one!!!
        } else {
            index2++;
        }
    }
    // below is the (index1 + index2 == k - 1) base case
    // also eliminate the risk of referring to an element outside of index boundary
    if (size1 > index1 && (size2 <= index2 || A1[index1].compareTo(A2[index2]) < 0)) {
        return A1[index1];
    } else {
        return A2[index2];
    }
}

Bây giờ chúng ta có thể thử một thuật toán nhanh hơn chạy ở O (log k). Tương tự, so sánh A1[k/2]với A2[k/2]; nếu A1[k/2]nhỏ hơn, thì tất cả các phần tử từ A1[0]đến A1[k/2]sẽ nằm trong túi của chúng ta. Ý tưởng là không chỉ cam kết một phần tử trong mỗi vòng lặp; bước đầu tiên chứa k/2các phần tử. Một lần nữa, chúng ta không thể bao gồm hoặc loại trừ A2[0]để A2[k/2]anyway. Vì vậy, trong bước đầu tiên, chúng ta không thể đi nhiều hơn k/2các phần tử. Đối với bước thứ hai, chúng ta không thể đi nhiều hơn k/4các yếu tố ...

Sau mỗi bước, chúng ta tiến gần hơn đến phần tử thứ k. Đồng thời mỗi bước nhỏ hơn và nhỏ hơn, cho đến khi chúng ta đạt được (step == 1), đó là (k-1 == index1+index2). Sau đó, chúng ta có thể tham khảo trường hợp cơ sở đơn giản và mạnh mẽ một lần nữa.

Đây là mã hoạt động chính xác:

private E kthSmallestFast(int k) {
    // System.out.println("this is an O(log k) speed algorithm with meaningful variables name");
    int size1 = A1.length, size2 = A2.length;

    int index1 = 0, index2 = 0, step = 0;
    while (index1 + index2 < k - 1) {
        step = (k - index1 - index2) / 2;
        int step1 = index1 + step;
        int step2 = index2 + step;
        if (size1 > step1 - 1
                && (size2 <= step2 - 1 || A1[step1 - 1].compareTo(A2[step2 - 1]) < 0)) {
            index1 = step1; // commit to element at index = step1 - 1
        } else {
            index2 = step2;
        }
    }
    // the base case of (index1 + index2 == k - 1)
    if (size1 > index1 && (size2 <= index2 || A1[index1].compareTo(A2[index2]) < 0)) {
        return A1[index1];
    } else {
        return A2[index2];
    }
}

Một số người có thể lo lắng điều gì sẽ xảy ra nếu (index1+index2)nhảy qua k-1? Chúng ta có thể bỏ lỡ trường hợp cơ sở(k-1 == index1+index2) ? Không thể nào. Bạn có thể cộng 0,5 + 0,25 + 0,125 ..., và bạn sẽ không bao giờ vượt quá 1.

Tất nhiên, rất dễ dàng để biến đoạn mã trên thành thuật toán đệ quy:

private E kthSmallestFastRecur(int k, int index1, int index2, int size1, int size2) {
    // System.out.println("this is an O(log k) speed algorithm with meaningful variables name");

    // the base case of (index1 + index2 == k - 1)
    if (index1 + index2 == k - 1) {
        if (size1 > index1 && (size2 <= index2 || A1[index1].compareTo(A2[index2]) < 0)) {
            return A1[index1];
        } else {
            return A2[index2];
        }
    }

    int step = (k - index1 - index2) / 2;
    int step1 = index1 + step;
    int step2 = index2 + step;
    if (size1 > step1 - 1 && (size2 <= step2 - 1 || A1[step1 - 1].compareTo(A2[step2 - 1]) < 0)) {
        index1 = step1;
    } else {
        index2 = step2;
    }
    return kthSmallestFastRecur(k, index1, index2, size1, size2);
}

Hy vọng phân tích trên và mã Java có thể giúp bạn hiểu. Nhưng đừng bao giờ sao chép mã của tôi làm bài tập về nhà của bạn! Chúc mừng;)


1
Cảm ơn bạn rất nhiều vì những lời giải thích và câu trả lời tuyệt vời của bạn, +1 :)
Hengameh

Trong mã đầu tiên, không nên else if (A1[size1 - 1].compareTo(A2[size2 - 1]) < 0) thay vì else if (A1[size1 - 1].compareTo(A2[size2 - 1]) > 0)? (Trong mã kthSmallestSlowWithFault)
Hengameh

Xin cảm ơn @Fei. Lời giải thích tuyệt vời. Thật ngạc nhiên là có bao nhiêu câu trả lời sai được lan truyền trên internet về vấn đề này. Điều đáng ngạc nhiên hơn nữa là câu trả lời được chấp nhận của cô ấy trên SO về câu hỏi này luôn là câu sai. Có vẻ như không ai quan tâm đến việc kiểm tra các câu trả lời.
Thuyền trưởng Fogetti

Có thể cắt bỏ giải pháp O (k) sau một số bước (đã nói là 15), vì phạm vi các bước giảm khá nhanh.
Sky

1
Trong không có lệnh gọi đệ quy nào, kích thước của A1 hoặc A2 bị giảm.
Aditya Joshee

5

Đây là phiên bản lặp lại C ++ của giải pháp @ lambdapilgrim (xem giải thích về thuật toán ở đó):

#include <cassert>
#include <iterator>

template<class RandomAccessIterator, class Compare>
typename std::iterator_traits<RandomAccessIterator>::value_type
nsmallest_iter(RandomAccessIterator firsta, RandomAccessIterator lasta,
               RandomAccessIterator firstb, RandomAccessIterator lastb,
               size_t n,
               Compare less) {
  assert(issorted(firsta, lasta, less) && issorted(firstb, lastb, less));
  for ( ; ; ) {
    assert(n < static_cast<size_t>((lasta - firsta) + (lastb - firstb)));
    if (firsta == lasta) return *(firstb + n);
    if (firstb == lastb) return *(firsta + n);

    size_t mida = (lasta - firsta) / 2;
    size_t midb = (lastb - firstb) / 2;
    if ((mida + midb) < n) {
      if (less(*(firstb + midb), *(firsta + mida))) {
        firstb += (midb + 1);
        n -= (midb + 1);
      }
      else {
        firsta += (mida + 1);
        n -= (mida + 1);
      }
    }
    else {
      if (less(*(firstb + midb), *(firsta + mida)))
        lasta = (firsta + mida);
      else
        lastb = (firstb + midb);
    }
  }
}

Nó hoạt động cho tất cả các 0 <= n < (size(a) + size(b))chỉ mục và có O(log(size(a)) + log(size(b)))độ phức tạp.

Thí dụ

#include <functional> // greater<>
#include <iostream>

#define SIZE(a) (sizeof(a) / sizeof(*a))

int main() {
  int a[] = {5,4,3};
  int b[] = {2,1,0};
  int k = 1; // find minimum value, the 1st smallest value in a,b

  int i = k - 1; // convert to zero-based indexing
  int v = nsmallest_iter(a, a + SIZE(a), b, b + SIZE(b),
                         SIZE(a)+SIZE(b)-1-i, std::greater<int>());
  std::cout << v << std::endl; // -> 0
  return v;
}

4

Nỗ lực của tôi cho k số đầu tiên, số thứ k trong 2 mảng đã sắp xếp và trong n mảng đã sắp xếp:

// require() is recognizable by node.js but not by browser;
// for running/debugging in browser, put utils.js and this file in <script> elements,
if (typeof require === "function") require("./utils.js");

// Find K largest numbers in two sorted arrays.
function k_largest(a, b, c, k) {
    var sa = a.length;
    var sb = b.length;
    if (sa + sb < k) return -1;
    var i = 0;
    var j = sa - 1;
    var m = sb - 1;
    while (i < k && j >= 0 && m >= 0) {
        if (a[j] > b[m]) {
            c[i] = a[j];
            i++;
            j--;
        } else {
            c[i] = b[m];
            i++;
            m--;
        }
    }
    debug.log(2, "i: "+ i + ", j: " + j + ", m: " + m);
    if (i === k) {
        return 0;
    } else if (j < 0) {
        while (i < k) {
            c[i++] = b[m--];
        }
    } else {
        while (i < k) c[i++] = a[j--];
    }
    return 0;
}

// find k-th largest or smallest number in 2 sorted arrays.
function kth(a, b, kd, dir){
    sa = a.length; sb = b.length;
    if (kd<1 || sa+sb < kd){
        throw "Mission Impossible! I quit!";
    }

    var k;
    //finding the kd_th largest == finding the smallest k_th;
    if (dir === 1){ k = kd;
    } else if (dir === -1){ k = sa + sb - kd + 1;}
    else throw "Direction has to be 1 (smallest) or -1 (largest).";

    return find_kth(a, b, k, sa-1, 0, sb-1, 0);
}

// find k-th smallest number in 2 sorted arrays;
function find_kth(c, d, k, cmax, cmin, dmax, dmin){

    sc = cmax-cmin+1; sd = dmax-dmin+1; k0 = k; cmin0 = cmin; dmin0 = dmin;
    debug.log(2, "=k: " + k +", sc: " + sc + ", cmax: " + cmax +", cmin: " + cmin + ", sd: " + sd +", dmax: " + dmax + ", dmin: " + dmin);

    c_comp = k0-sc;
    if (c_comp <= 0){
        cmax = cmin0 + k0-1;
    } else {
        dmin = dmin0 + c_comp-1;
        k -= c_comp-1;
    }

    d_comp = k0-sd;
    if (d_comp <= 0){
        dmax = dmin0 + k0-1;
    } else {
        cmin = cmin0 + d_comp-1;
        k -= d_comp-1;
    }
    sc = cmax-cmin+1; sd = dmax-dmin+1;

    debug.log(2, "#k: " + k +", sc: " + sc + ", cmax: " + cmax +", cmin: " + cmin + ", sd: " + sd +", dmax: " + dmax + ", dmin: " + dmin + ", c_comp: " + c_comp + ", d_comp: " + d_comp);

    if (k===1) return (c[cmin]<d[dmin] ? c[cmin] : d[dmin]);
    if (k === sc+sd) return (c[cmax]>d[dmax] ? c[cmax] : d[dmax]);

    m = Math.floor((cmax+cmin)/2);
    n = Math.floor((dmax+dmin)/2);

    debug.log(2, "m: " + m + ", n: "+n+", c[m]: "+c[m]+", d[n]: "+d[n]);

    if (c[m]<d[n]){
        if (m === cmax){ // only 1 element in c;
            return d[dmin+k-1];
        }

        k_next = k-(m-cmin+1);
        return find_kth(c, d, k_next, cmax, m+1, dmax, dmin);
    } else {
        if (n === dmax){
            return c[cmin+k-1];
        }

        k_next = k-(n-dmin+1);
        return find_kth(c, d, k_next, cmax, cmin, dmax, n+1);
    }
}

function traverse_at(a, ae, h, l, k, at, worker, wp){
    var n = ae ? ae.length : 0;
    var get_node;
    switch (at){
        case "k": get_node = function(idx){
                var node = {};
                var pos = l[idx] + Math.floor(k/n) - 1;
                if (pos<l[idx]){ node.pos = l[idx]; }
                else if (pos > h[idx]){ node.pos = h[idx];}
                else{ node.pos = pos; }

                node.idx = idx;
                node.val = a[idx][node.pos];
                debug.log(6, "pos: "+pos+"\nnode =");
                debug.log(6, node);
                return node;
            };
            break;
        case "l": get_node = function(idx){
                debug.log(6, "a["+idx+"][l["+idx+"]]: "+a[idx][l[idx]]);
                return a[idx][l[idx]];
            };
            break;
        case "h": get_node = function(idx){
                debug.log(6, "a["+idx+"][h["+idx+"]]: "+a[idx][h[idx]]);
                return a[idx][h[idx]];
            };
            break;
        case "s": get_node = function(idx){
                debug.log(6, "h["+idx+"]-l["+idx+"]+1: "+(h[idx] - l[idx] + 1));
                return h[idx] - l[idx] + 1;
            };
            break;
        default: get_node = function(){
                debug.log(1, "!!! Exception: get_node() returns null.");
                return null;
            };
            break;
    }

    worker.init();

    debug.log(6, "--* traverse_at() *--");

    var i;
    if (!wp){
        for (i=0; i<n; i++){
            worker.work(get_node(ae[i]));
        }    
    } else {
        for (i=0; i<n; i++){
            worker.work(get_node(ae[i]), wp);
        }
    }

    return worker.getResult();
}

sumKeeper = function(){
    var res = 0;
    return {
        init     : function(){ res = 0;},
        getResult: function(){
                debug.log(5, "@@ sumKeeper.getResult: returning: "+res);
                return res;
            },
        work     : function(node){ if (node!==null) res += node;}
    };
}();

maxPicker = function(){
    var res = null;
    return {
        init     : function(){ res = null;},
        getResult: function(){
                debug.log(5, "@@ maxPicker.getResult: returning: "+res);
                return res;
            },
        work     : function(node){
            if (res === null){ res = node;}
            else if (node!==null && node > res){ res = node;}
        }
    };    
}();

minPicker = function(){
    var res = null;
    return {
        init     : function(){ res = null;},
        getResult: function(){
                debug.log(5, "@@ minPicker.getResult: returning: ");
                debug.log(5, res);
                return res;
            },
        work     : function(node){
            if (res === null && node !== null){ res = node;}
            else if (node!==null &&
                node.val !==undefined &&
                node.val < res.val){ res = node; }
            else if (node!==null && node < res){ res = node;}
        }
    };  
}();

// find k-th smallest number in n sorted arrays;
// need to consider the case where some of the subarrays are taken out of the selection;
function kth_n(a, ae, k, h, l){
    var n = ae.length;
    debug.log(2, "------**  kth_n()  **-------");
    debug.log(2, "n: " +n+", k: " + k);
    debug.log(2, "ae: ["+ae+"],  len: "+ae.length);
    debug.log(2, "h: [" + h + "]");
    debug.log(2, "l: [" + l + "]");

    for (var i=0; i<n; i++){
        if (h[ae[i]]-l[ae[i]]+1>k) h[ae[i]]=l[ae[i]]+k-1;
    }
    debug.log(3, "--after reduction --");
    debug.log(3, "h: [" + h + "]");
    debug.log(3, "l: [" + l + "]");

    if (n === 1)
        return a[ae[0]][k-1]; 
    if (k === 1)
        return traverse_at(a, ae, h, l, k, "l", minPicker);
    if (k === traverse_at(a, ae, h, l, k, "s", sumKeeper))
        return traverse_at(a, ae, h, l, k, "h", maxPicker);

    var kn = traverse_at(a, ae, h, l, k, "k", minPicker);
    debug.log(3, "kn: ");
    debug.log(3, kn);

    var idx = kn.idx;
    debug.log(3, "last: k: "+k+", l["+kn.idx+"]: "+l[idx]);
    k -= kn.pos - l[idx] + 1;
    l[idx] = kn.pos + 1;
    debug.log(3, "next: "+"k: "+k+", l["+kn.idx+"]: "+l[idx]);
    if (h[idx]<l[idx]){ // all elements in a[idx] selected;
        //remove a[idx] from the arrays.
        debug.log(4, "All elements selected in a["+idx+"].");
        debug.log(5, "last ae: ["+ae+"]");
        ae.splice(ae.indexOf(idx), 1);
        h[idx] = l[idx] = "_"; // For display purpose only.
        debug.log(5, "next ae: ["+ae+"]");
    }

    return kth_n(a, ae, k, h, l);
}

function find_kth_in_arrays(a, k){

    if (!a || a.length<1 || k<1) throw "Mission Impossible!";

    var ae=[], h=[], l=[], n=0, s, ts=0;
    for (var i=0; i<a.length; i++){
        s = a[i] && a[i].length;
        if (s>0){
            ae.push(i); h.push(s-1); l.push(0);
            ts+=s;
        }
    }

    if (k>ts) throw "Too few elements to choose from!";

    return kth_n(a, ae, k, h, l);
}

/////////////////////////////////////////////////////
// tests
// To show everything: use 6.
debug.setLevel(1);

var a = [2, 3, 5, 7, 89, 223, 225, 667];
var b = [323, 555, 655, 673];
//var b = [99];
var c = [];

debug.log(1, "a = (len: " + a.length + ")");
debug.log(1, a);
debug.log(1, "b = (len: " + b.length + ")");
debug.log(1, b);

for (var k=1; k<a.length+b.length+1; k++){
    debug.log(1, "================== k: " + k + "=====================");

    if (k_largest(a, b, c, k) === 0 ){
      debug.log(1, "c = (len: "+c.length+")");
      debug.log(1, c);
    }

    try{
        result = kth(a, b, k, -1);
        debug.log(1, "===== The " + k + "-th largest number: " + result);
    } catch (e) {
        debug.log(0, "Error message from kth(): " + e);
    }
    debug.log("==================================================");
}

debug.log(1, "################# Now for the n sorted arrays ######################");
debug.log(1, "####################################################################");

x = [[1, 3, 5, 7, 9],
     [-2, 4, 6, 8, 10, 12],
     [8, 20, 33, 212, 310, 311, 623],
     [8],
     [0, 100, 700],
     [300],
     [],
     null];

debug.log(1, "x = (len: "+x.length+")");
debug.log(1, x);

for (var i=0, num=0; i<x.length; i++){
    if (x[i]!== null) num += x[i].length;
}
debug.log(1, "totoal number of elements: "+num);

// to test k in specific ranges:
var start = 0, end = 25;
for (k=start; k<end; k++){
    debug.log(1, "=========================== k: " + k + "===========================");

    try{
        result = find_kth_in_arrays(x, k);
        debug.log(1, "====== The " + k + "-th smallest number: " + result);
    } catch (e) {
        debug.log(1, "Error message from find_kth_in_arrays: " + e);
    }
    debug.log(1, "=================================================================");
}
debug.log(1, "x = (len: "+x.length+")");
debug.log(1, x);
debug.log(1, "totoal number of elements: "+num);

Bạn có thể tìm thấy mã hoàn chỉnh với utils gỡ lỗi tại: https://github.com/brainclone/teasers/tree/master/kth


3

Đây là mã của tôi dựa trên giải pháp của Jules Olleon:

int getNth(vector<int>& v1, vector<int>& v2, int n)
{
    int step = n / 4;

    int i1 = n / 2;
    int i2 = n - i1;

    while(!(v2[i2] >= v1[i1 - 1] && v1[i1] > v2[i2 - 1]))
    {                   
        if (v1[i1 - 1] >= v2[i2 - 1])
        {
            i1 -= step;
            i2 += step;
        }
        else
        {
            i1 += step;
            i2 -= step;
        }

        step /= 2;
        if (!step) step = 1;
    }

    if (v1[i1 - 1] >= v2[i2 - 1])
        return v1[i1 - 1];
    else
        return v2[i2 - 1];
}

int main()  
{  
    int a1[] = {1,2,3,4,5,6,7,8,9};
    int a2[] = {4,6,8,10,12};

    //int a1[] = {1,2,3,4,5,6,7,8,9};
    //int a2[] = {4,6,8,10,12};

    //int a1[] = {1,7,9,10,30};
    //int a2[] = {3,5,8,11};
    vector<int> v1(a1, a1+9);
    vector<int> v2(a2, a2+5);


    cout << getNth(v1, v2, 5);
    return 0;  
}  

1
Điều này sẽ không hoạt động đối với một số trường hợp. Ví dụ, int a2 [] = {1,2,3,4, 5}; int a1 [] = {5,6,8,10,12}; getNth (a1, a2, 7). Chỉ mục của mảng sẽ đi ra ngoài ranh giới.
Jay

2

Đây là cách triển khai của tôi trong C, bạn có thể tham khảo giải thích của @Jules Olléon cho thuật toán: ý tưởng đằng sau thuật toán là chúng ta duy trì i + j = k và tìm i và j như vậy để a [i-1] <b [j-1] <a [i] (hoặc ngược lại). Bây giờ vì có i phần tử trong 'a' nhỏ hơn b [j-1] và j-1 phần tử trong 'b' nhỏ hơn b [j-1], b [j-1] là i + j-1 + 1 = phần tử nhỏ nhất thứ k. Để tìm i, j như vậy, thuật toán thực hiện tìm kiếm lưỡng phân trên các mảng.

int find_k(int A[], int m, int B[], int n, int k) {
   if (m <= 0 )return B[k-1];
   else if (n <= 0) return A[k-1];
   int i =  ( m/double (m + n))  * (k-1);
   if (i < m-1 && i<k-1) ++i;
   int j = k - 1 - i;

   int Ai_1 = (i > 0) ? A[i-1] : INT_MIN, Ai = (i<m)?A[i]:INT_MAX;
   int Bj_1 = (j > 0) ? B[j-1] : INT_MIN, Bj = (j<n)?B[j]:INT_MAX;
   if (Ai >= Bj_1 && Ai <= Bj) {
       return Ai;
   } else if (Bj >= Ai_1 && Bj <= Ai) {
       return Bj;
   }
   if (Ai < Bj_1) { // the answer can't be within A[0,...,i]
       return find_k(A+i+1, m-i-1, B, n, j);
   } else { // the answer can't be within A[0,...,i]
       return find_k(A, m, B+j+1, n-j-1, i);
   }
 }

2

Đây là giải pháp của tôi. Mã C ++ in ra giá trị nhỏ nhất thứ k cũng như số lần lặp để nhận được giá trị nhỏ nhất thứ k bằng cách sử dụng một vòng lặp, theo ý kiến ​​của tôi là theo thứ tự log (k). Tuy nhiên, mã yêu cầu k phải nhỏ hơn độ dài của mảng đầu tiên, đây là một hạn chế.

#include <iostream>
#include <vector>
#include<math.h>
using namespace std;

template<typename comparable>
comparable kthSmallest(vector<comparable> & a, vector<comparable> & b, int k){

int idx1; // Index in the first array a
int idx2; // Index in the second array b
comparable maxVal, minValPlus;
float iter = k;
int numIterations = 0;

if(k > a.size()){ // Checks if k is larger than the size of first array
    cout << " k is larger than the first array" << endl;
    return -1;
}
else{ // If all conditions are satisfied, initialize the indexes
    idx1 = k - 1;
    idx2 = -1;
}

for ( ; ; ){
    numIterations ++;
    if(idx2 == -1 || b[idx2] <= a[idx1] ){
        maxVal = a[idx1];
        minValPlus = b[idx2 + 1];
        idx1 = idx1 - ceil(iter/2); // Binary search
        idx2 = k - idx1 - 2; // Ensures sum of indices  = k - 2
    }
    else{
        maxVal = b[idx2];
        minValPlus = a[idx1 + 1];
        idx2 = idx2 - ceil(iter/2); // Binary search
        idx1 = k - idx2 - 2; // Ensures sum of indices  = k - 2
    }
    if(minValPlus >= maxVal){ // Check if kth smallest value has been found
        cout << "The number of iterations to find the " << k << "(th) smallest value is    " << numIterations << endl;
        return maxVal;

    }
    else
        iter/=2; // Reduce search space of binary search
   }
}

int main(){
//Test Cases
    vector<int> a = {2, 4, 9, 15, 22, 34, 45, 55, 62, 67, 78, 85};
    vector<int> b = {1, 3, 6, 8, 11, 13, 15, 20, 56, 67, 89};
    // Input k < a.size()
    int kthSmallestVal;
    for (int k = 1; k <= a.size() ; k++){
        kthSmallestVal = kthSmallest<int>( a ,b ,k );
        cout << k <<" (th) smallest Value is " << kthSmallestVal << endl << endl << endl;
    }
}

1

Mã giả đầu tiên được cung cấp ở trên, không hoạt động với nhiều giá trị. Ví dụ, đây là hai mảng. int [] a = {1, 5, 6, 8, 9, 11, 15, 17, 19}; int [] b = {4, 7, 8, 13, 15, 18, 20, 24, 26};

Nó không hoạt động đối với k = 3 và k = 9 trong đó. Tôi có một giải pháp khác. Nó được đưa ra dưới đây.

private static void traverse(int pt, int len) {
int temp = 0;

if (len == 1) {
    int val = 0;
    while (k - (pt + 1) - 1 > -1 && M[pt] < N[k - (pt + 1) - 1]) {

    if (val == 0)
        val = M[pt] < N[k - (pt + 1) - 1] ? N[k - (pt + 1) - 1]
            : M[pt];
    else {
        int t = M[pt] < N[k - (pt + 1) - 1] ? N[k - (pt + 1) - 1]
            : M[pt];
        val = val < t ? val : t;

    }

    ++pt;
    }

    if (val == 0)
    val = M[pt] < N[k - (pt + 1) - 1] ? N[k - (pt + 1) - 1] : M[pt];

    System.out.println(val);
    return;
}

temp = len / 2;

if (M[pt + temp - 1] < N[k - (pt + temp) - 1]) {
    traverse(pt + temp, temp);

} else {
    traverse(pt, temp);
}

}

Nhưng ... nó cũng không hoạt động với k = 5. Việc bắt chẵn / lẻ này không cho phép nó trở nên đơn giản.


1
public class KthSmallestInSortedArray {

    public static void main(String[] args) {
        int a1[] = {2, 3, 10, 11, 43, 56},
                a2[] = {120, 13, 14, 24, 34, 36},
                k = 4;

        System.out.println(findKthElement(a1, a2, k));

    }

    private static int findKthElement(int a1[], int a2[], int k) {

        /** Checking k must less than sum of length of both array **/
        if (a1.length + a2.length < k) {
            throw new IllegalArgumentException();
        }

        /** K must be greater than zero **/
        if (k <= 0) {
            throw new IllegalArgumentException();
        }

        /**
         * Finding begin, l and end such that
         * begin <= l < end
         * a1[0].....a1[l-1] and
         * a2[0]....a2[k-l-1] are the smallest k numbers
         */
        int begin = Math.max(0, k - a2.length);
        int end = Math.min(a1.length, k);

        while (begin < end) {
            int l = begin + (end - begin) / 2;

            /** Can we include a1[l] in the k smallest numbers */
            if ((l < a1.length) &&
                    (k - l > 0) &&
                    (a1[l] < a2[k - l - 1])) {

                begin = l + 1;

            } else if ((l > 0) &&
                    (k - l < a2.length) &&
                    (a1[l - 1] > a2[k - 1])) {

                /**
                 * This is the case where we can discard
                 * a[l-1] from the set of k smallest numbers
                 */
                end = l;

            } else {

                /**
                 * We found our answer since both inequalities were
                 * false
                 */
                begin = l;
                break;
            }
        }

        if (begin == 0) {
            return a2[k - 1];
        } else if (begin == k) {
            return a1[k - 1];
        } else {
            return Math.max(a1[begin - 1], a2[k - begin - 1]);
        }
    }
}

1

Đây là giải pháp của tôi trong java. Sẽ cố gắng tối ưu hóa nó hơn nữa

  public class FindKLargestTwoSortedArray {

    public static void main(String[] args) {
        int[] arr1 = { 10, 20, 40, 80 };
        int[] arr2 = { 15, 35, 50, 75 };

    FindKLargestTwoSortedArray(arr1, 0, arr1.length - 1, arr2, 0,
            arr2.length - 1, 6);
    }


    public static void FindKLargestTwoSortedArray(int[] arr1, int start1,
            int end1, int[] arr2, int start2, int end2, int k) {

        if ((start1 <= end1 && start1 >= 0 && end1 < arr1.length)
                && (start2 <= end2 && start2 >= 0 && end2 < arr2.length)) {

            int midIndex1 = (start1 + (k - 1) / 2);
            midIndex1 = midIndex1 >= arr1.length ? arr1.length - 1 : midIndex1;
            int midIndex2 = (start2 + (k - 1) / 2);
            midIndex2 = midIndex2 >= arr2.length ? arr2.length - 1 : midIndex2;


            if (arr1[midIndex1] == arr2[midIndex2]) {
                System.out.println("element is " + arr1[midIndex1]);
            } else if (arr1[midIndex1] < arr2[midIndex2]) {

                if (k == 1) {
                    System.out.println("element is " + arr1[midIndex1]);
                    return;
                } else if (k == 2) {
                    System.out.println("element is " + arr2[midIndex2]);
                    return;
                }else if (midIndex1 == arr1.length-1 || midIndex2 == arr2.length-1 ) {
                    if(k==(arr1.length+arr2.length)){
                    System.out.println("element is " + arr2[midIndex2]);
                    return;
                    }else if(k==(arr1.length+arr2.length)-1){
                        System.out.println("element is " + arr1[midIndex1]);
                        return;
                    }

                }

                int remainingElementToSearch = k - (midIndex1-start1);
                FindKLargestTwoSortedArray(
                        arr1,
                        midIndex1,
                        (midIndex1 + remainingElementToSearch) >= arr1.length ? arr1.length-1
                                : (midIndex1 + remainingElementToSearch), arr2,
                        start2, midIndex2, remainingElementToSearch);

            } else if (arr1[midIndex1] > arr2[midIndex2]) {
                FindKLargestTwoSortedArray(arr2, start2, end2, arr1, start1,
                        end1, k);
            }

        } else {
            return;
        }

    }
}

Điều này được lấy cảm hứng từ Algo tại video youtube tuyệt vời


1

Liên kết tới mã độ phức tạp của (log (n) + log (m))

Liên kết với mã (log (n) * log (m))

Thực hiện pháp (log (n) + log (m))

Tôi muốn thêm lời giải thích của tôi cho vấn đề. Đây là một bài toán cổ điển mà chúng ta phải sử dụng thực tế là hai mảng được sắp xếp. chúng tôi đã được cung cấp hai mảng được sắp xếp arr1 có kích thước sz1 và arr2 có kích thước sz2

a) Giả sử nếu

Kiểm tra Nếu k hợp lệ

k là> (sz1 + sz2)

thì chúng ta không thể tìm thấy phần tử nhỏ nhất thứ k trong liên hiệp của cả hai mảng đã sắp xếp ryt Vì vậy, trả về Dữ liệu không hợp lệ. b) Bây giờ nếu điều kiện trên là sai và chúng ta có giá trị hợp lệ và khả thi của k,

Quản lý các trường hợp cạnh

Chúng tôi sẽ nối cả mảng với giá trị-vô cực ở phía trước và + giá trị vô cực ở cuối để bao gồm các trường hợp cạnh của k = 1,2 và k = (sz1 + sz2-1), (sz1 + sz2), v.v.

Bây giờ cả hai mảng có kích thước (sz1 + 2)(sz2 + 2) tương ứng

Thuật toán chính

Bây giờ, chúng ta sẽ thực hiện tìm kiếm nhị phân trên arr1. Chúng ta sẽ thực hiện tìm kiếm nhị phân trên arr1 để tìm chỉ mục i, startIndex <= i <= endIndex

sao cho nếu chúng ta tìm thấy chỉ mục j tương ứng trong arr2 bằng cách sử dụng ràng buộc {(i + j) = k}, thì nếu

if (arr2 [j-1] <arr1 [i] <arr2 [j]) , thì arr1 [i] là nhỏ nhất thứ k (Trường hợp 1)

else if (arr1 [i-1] <arr2 [j] <arr1 [i]) , thì arr2 [i] là nhỏ nhất thứ k (Trường hợp 2)

khác ký hiệu arr1 [i] <arr2 [j-1] <arr2 [j] (Case3)

hoặc arr2 [j-1] <arr2 [j] <arr1 [i] (Case4)

Vì chúng ta biết rằng phần tử nhỏ nhất thứ k có (k-1) phần tử nhỏ hơn nó trong liên kết của cả hai mảng ryt? Vì thế,

Trong Case1 , những gì chúng tôi đã làm, chúng tôi đảm bảo rằng có tổng số (k-1) phần tử nhỏ hơn đối với arr1 [i] vì các phần tử nhỏ hơn arr1 [i] trong mảng arr1 có số lượng là i-1 so với chúng tôi biết (arr2 [ j-1] <arr1 [i] <arr2 [j]) và số phần tử nhỏ hơn arr1 [i] trong arr2 là j-1 vì j được tìm thấy bằng cách sử dụng (i-1) + (j-1) = (k -1) Vì vậy phần tử nhỏ nhất thứ k sẽ là arr1 [i]

Nhưng câu trả lời có thể không phải lúc nào cũng đến từ mảng đầu tiên tức là arr1 nên chúng tôi đã kiểm tra case2 cũng thỏa mãn tương tự như trường hợp 1 vì (i-1) + (j-1) = (k-1). Bây giờ nếu chúng ta có (arr1 [i-1] <arr2 [j] <arr1 [i]), chúng ta có tổng k-1 phần tử nhỏ hơn arr2 [j] trong liên hợp của cả hai mảng nên nó là phần tử nhỏ nhất thứ k.

Trong case3 , để tạo nó với bất kỳ trường hợp 1 hoặc trường hợp 2, chúng ta cần phải tăng i và j sẽ được tìm thấy theo sử dụng hạn chế {(i + j) = k} nghĩa trong di chuyển tìm kiếm nhị phân để phần bên phải tức là làm cho startIndex = middleIndex

Trong case4 , để tạo nó với bất kỳ trường hợp 1 hoặc trường hợp 2, chúng ta cần phải giảm giá trị i và j sẽ được tìm thấy theo sử dụng hạn chế {(i + j) = k} nghĩa trong di chuyển tìm kiếm nhị phân để phần bên trái tức là làm cho endIndex = middleIndex .

Bây giờ làm thế nào để quyết định startIndex và endIndex khi bắt đầu tìm kiếm nhị phân trên arr1 với startindex = 1 và endIndex = ??. Chúng ta cần quyết định.

Nếu k> sz1, endIndex = (sz1 + 1), else endIndex = k;

Bởi vì nếu k lớn hơn kích thước của mảng đầu tiên, chúng ta có thể phải thực hiện tìm kiếm nhị phân trên toàn bộ mảng arr1 khác, chúng ta chỉ cần lấy k phần tử đầu tiên của nó bởi vì sz1-k phần tử không bao giờ có thể đóng góp trong việc tính toán thứ k nhỏ nhất.

MÃ được hiển thị bên dưới

// Complexity    O(log(n)+log(m))

#include<bits/stdc++.h>
using namespace std;
#define f(i,x,y) for(int i = (x);i < (y);++i)
#define F(i,x,y) for(int i = (x);i > (y);--i)
int max(int a,int b){return (a > b?a:b);}
int min(int a,int b){return (a < b?a:b);}
int mod(int a){return (a > 0?a:((-1)*(a)));}
#define INF 1000000




int func(int *arr1,int *arr2,int sz1,int sz2,int k)

{

if((k <= (sz1+sz2))&&(k > 0))

{
int s = 1,e,i,j;
if(k > sz1)e = sz1+1;
else e = k;
while((e-s)>1)
{
  i = (e+s)/2;
  j = ((k-1)-(i-1)); 
  j++;
  if(j > (sz2+1)){s = i;}
  else if((arr1[i] >= arr2[j-1])&&(arr1[i] <= arr2[j]))return arr1[i];
  else if((arr2[j] >= arr1[i-1])&&(arr2[j] <= arr1[i]))return arr2[j];
  else if(arr1[i] < arr2[j-1]){s = i;}
  else if(arr1[i] > arr2[j]){e = i;}
  else {;}
}
i = e,j = ((k-1)-(i-1));j++;
if((arr1[i] >= arr2[j-1])&&(arr1[i] <= arr2[j]))return arr1[i];
else if((arr2[j] >= arr1[i-1])&&(arr2[j] <= arr1[i]))return arr2[j];
else
{
  i = s,j = ((k-1)-(i-1));j++;
  if((arr1[i] >= arr2[j-1])&&(arr1[i] <= arr2[j]))return arr1[i];
  else return arr2[j];
}

  }

 else

{
cout << "Data Invalid" << endl;
return -INF;

}

}





int main()

{
int n,m,k;
cin >> n >> m >> k;
int arr1[n+2];
int arr2[m+2];
f(i,1,n+1)
cin >> arr1[i];
f(i,1,m+1)
cin >> arr2[i];
arr1[0] = -INF;
arr2[0] = -INF;
  arr1[n+1] = +INF;  
arr2[m+1] = +INF; 
int val = func(arr1,arr2,n,m,k);
if(val != -INF)cout << val << endl;   
return 0;

}

Đối với Giải pháp của độ phức tạp (log (n) * log (m))

Chỉ là tôi đã bỏ lỡ việc sử dụng lợi thế của thực tế là đối với mỗi tôi, j có thể được tìm thấy bằng cách sử dụng ràng buộc {(i-1) + (j-1) = (k-1)} Vì vậy, đối với mỗi ii đã áp dụng thêm tìm kiếm nhị phân trên mảng thứ hai để tìm j sao cho arr2 [j] <= arr1 [i]. Vì vậy giải pháp này có thể được tối ưu hóa hơn nữa


1

Về cơ bản, thông qua cách tiếp cận này, bạn có thể loại bỏ k / 2 phần tử ở mỗi bước. K sẽ thay đổi một cách đệ quy từ k => k / 2 => k / 4 => ... cho đến khi nó đạt đến 1. Vì vậy, Độ phức tạp thời gian là O (logk)

Tại k = 1, chúng ta nhận được giá trị thấp nhất trong hai mảng.

Đoạn mã sau là trong JAVA. Xin lưu ý rằng chúng tôi đang trừ 1 (-1) trong mã khỏi các chỉ số vì chỉ mục của mảng Java bắt đầu từ 0 chứ không phải 1, ví dụ. k = 3 được đại diện bởi phần tử trong chỉ số thứ 2 của một mảng.

private int kthElement(int[] arr1, int[] arr2, int k) {
        if (k < 1 || k > (arr1.length + arr2.length))
            return -1;
        return helper(arr1, 0, arr1.length - 1, arr2, 0, arr2.length - 1, k);
    }


private int helper(int[] arr1, int low1, int high1, int[] arr2, int low2, int high2, int k) {
    if (low1 > high1) {
        return arr2[low2 + k - 1];
    } else if (low2 > high2) {
        return arr1[low1 + k - 1];
    }
    if (k == 1) {
        return Math.min(arr1[low1], arr2[low2]);
    }
    int i = Math.min(low1 + k / 2, high1 + 1);
    int j = Math.min(low2 + k / 2, high2 + 1);
    if (arr1[i - 1] > arr2[j - 1]) {
        return helper(arr1, low1, high1, arr2, j, high2, k - (j - low2));
    } else {
        return helper(arr1, i, high1, arr2, low2, high2, k - (i - low1));
    }
}

1
#include <bits/stdc++.h>
using namespace std;

int findKthElement(int a[],int start1,int end1,int b[],int start2,int end2,int k){

    if(start1 >= end1)return b[start2+k-1];
    if(start2 >= end2)return a[start1+k-1];
    if(k==1)return min(a[start1],b[start2]);
    int aMax = INT_MAX;
    int bMax = INT_MAX;
    if(start1+k/2-1 < end1) aMax = a[start1 + k/2 - 1];
    if(start2+k/2-1 < end2) bMax = b[start2 + k/2 - 1];

    if(aMax > bMax){
        return findKthElement(a,start1,end1,b,start2+k/2,end2,k-k/2);
    }
    else{
        return findKthElement(a,start1 + k/2,end1,b,start2,end2,k-k/2);
    }
}

int main(void){
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m,k;
        cout<<"Enter the size of 1st Array"<<endl;
        cin>>n;
        int arr[n];
        cout<<"Enter the Element of 1st Array"<<endl;
        for(int i = 0;i<n;i++){
            cin>>arr[i];
        }
        cout<<"Enter the size of 2nd Array"<<endl;
        cin>>m;
        int arr1[m];
        cout<<"Enter the Element of 2nd Array"<<endl;
        for(int i = 0;i<m;i++){
            cin>>arr1[i];
        }
        cout<<"Enter The Value of K";
        cin>>k;
        sort(arr,arr+n);
        sort(arr1,arr1+m);
        cout<<findKthElement(arr,0,n,arr1,0,m,k)<<endl;
    }

    return 0;
}

Độ phức tạp thời gian là O (log (min (n, m)))


1

Hầu hết các câu trả lời tôi tìm thấy ở đây đều tập trung vào cả hai mảng. mặc dù nó tốt nhưng khó thực hiện hơn vì có rất nhiều trường hợp phức tạp mà chúng ta cần phải quan tâm. Bên cạnh đó, hầu hết các triển khai là đệ quy, điều này làm tăng thêm độ phức tạp về không gian của ngăn xếp đệ quy. Vì vậy, thay vì tập trung vào cả hai mảng, tôi quyết định chỉ tập trung vào mảng nhỏ hơn và thực hiện tìm kiếm nhị phân chỉ trên mảng nhỏ hơn và điều chỉnh con trỏ cho mảng thứ hai dựa trên giá trị của con trỏ trong Mảng đầu tiên. Bằng việc thực hiện sau đây, chúng tôi có sự phức tạp của O(log(min(n,m))với O(1)độ phức tạp không gian.

    public static int kth_two_sorted(int []a, int b[],int k){
    if(a.length > b.length){
        return kth_two_sorted(b,a,k);
    }
    if(a.length + a.length < k){
        throw new RuntimeException("wrong argument");
    }
    int low = 0;
    int high = k;
    if(a.length <= k){
        high = a.length-1;
    }
    while(low <= high){
        int sizeA = low+(high - low)/2;
        int sizeB = k - sizeA;
        boolean shrinkLeft = false;
        boolean extendRight = false;
        if(sizeA != 0){
            if(sizeB !=b.length){
                if(a[sizeA-1] > b[sizeB]){
                    shrinkLeft = true;
                    high = sizeA-1;
                }
            }
        }
        if(sizeA!=a.length){
            if(sizeB!=0){
                if(a[sizeA] < b[sizeB-1]){
                    extendRight = true;
                    low = sizeA;
                }
            }
        }
        if(!shrinkLeft && !extendRight){
            return Math.max(a[sizeA-1],b[sizeB-1]) ;
        }
    }
    throw  new IllegalArgumentException("we can't be here");
}

Chúng tôi có một phạm vi [low, high]cho mảng avà chúng tôi thu hẹp phạm vi này khi chúng tôi đi xa hơn thông qua thuật toán. sizeAcho biết có bao nhiêu mục từ kcác mục là từ mảng avà nó bắt nguồn từ giá trị của lowhigh. sizeBlà cùng một định nghĩa ngoại trừ chúng tôi tính toán giá trị theo cách đó sizeA+sizeB=k. Dựa trên các giá trị trên hai đường viền đó để kết luận rằng chúng ta phải mở rộng sang bên phải trong mảng ahoặc thu nhỏ sang bên trái. nếu chúng tôi bị mắc kẹt ở cùng một vị trí, điều đó có nghĩa là chúng tôi đã tìm ra giải pháp và chúng tôi sẽ trả về giá trị tối đa ở vị trí sizeA-1từ asizeB-1từ b.


0

Kiểm tra mã này.

import math
def findkthsmallest():

    A=[1,5,10,22,30,35,75,125,150,175,200]
    B=[15,16,20,22,25,30,100,155,160,170]
    lM=0
    lN=0
    hM=len(A)-1
    hN=len(B)-1
    k=17

    while True:
        if k==1:
            return min(A[lM],B[lN])


        cM=hM-lM+1
        cN=hN-lN+1
        tmp = cM/float(cM+cN)
        iM=int(math.ceil(tmp*k))
        iN=k-iM
        iM=lM+iM-1
        iN=lN+iN-1
        if A[iM] >= B[iN]:
            if iN == hN or A[iM] < B[iN+1]:
                return A[iM]
            else:
                k = k - (iN-lN+1)
                lN=iN+1
                hM=iM-1
        if B[iN] >= A[iM]:
            if iM == hM or B[iN] < A[iM+1]:
                return B[iN]
            else:
                k = k - (iM-lM+1)
                lM=iM+1
                hN=iN-1
        if hM < lM:
            return B[lN+k-1]
        if hN < lN:
            return A[lM+k-1]

if __name__ == '__main__':
    print findkthsmallest();

Cung cấp lời giải thích
Abhijit Sarkar

0

Dưới đây mã C # để Tìm Phần tử Nhỏ nhất thứ k trong Liên hiệp Hai Mảng đã Sắp xếp. Độ phức tạp về thời gian: O (logk)

        public static int findKthSmallestElement1(int[] A, int startA, int endA, int[] B, int startB, int endB, int k)
        {
            int n = endA - startA;
            int m = endB - startB;

            if (n <= 0)
                return B[startB + k - 1];
            if (m <= 0)
                return A[startA + k - 1];
            if (k == 1)
                return A[startA] < B[startB] ? A[startA] : B[startB];

            int midA = (startA + endA) / 2;
            int midB = (startB + endB) / 2;

            if (A[midA] <= B[midB])
            {
                if (n / 2 + m / 2 + 1 >= k)
                    return findKthSmallestElement1(A, startA, endA, B, startB, midB, k);
                else
                    return findKthSmallestElement1(A, midA + 1, endA, B, startB, endB, k - n / 2 - 1);
            }
            else
            {
                if (n / 2 + m / 2 + 1 >= k)
                    return findKthSmallestElement1(A, startA, midA, B, startB, endB, k);
                else
                    return findKthSmallestElement1(A, startA, endA, B, midB + 1, endB, k - m / 2 - 1);

            }
        }

không có lỗi, tôi đã kiểm tra mã của mình trước khi tôi đăng lên SO
Piyush Patel

1
Cảm ơn sammy333, mình đã cập nhật mã. bây giờ nó đang hoạt động một
Piyush Patel

(Không tính midAtừ endAnếu k < nCheck for mảng ngắn, bắt đầu với. return B[startB + k - 1];.)
lọ
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.