Jak znaleźć k-ty najmniejszy element w unii dwóch posortowanych tablic?

106

To jest zadanie domowe. Mówią, że to zajmie O(logN + logM)gdzie NiM są długościami tablic.

Nazwijmy tablice ai b. Oczywiście możemy zignorować wszystko a[i]i b[i]gdzie i> k.
Najpierw porównajmy a[k/2]i b[k/2]. Niech b[k/2]> a[k/2]. Dlatego możemy odrzucić również wszystko b[i], gdzie i> k / 2.

Teraz mamy wszystko a[i], gdzie i <k i all b[i], gdzie i <k / 2, aby znaleźć odpowiedź.

Jaki jest następny krok?

Michael
źródło
6
Czy wszystkie te kroki zostały uwzględnione w przypisaniu, czy też powyższe kroki są początkiem algorytmu?
Kendrick,
18
Powyższe kroki są moje.
Michael
Czy O(logN + logM)odnosi się tylko do czasu potrzebnego na znalezienie k-tego elementu? Czy można wcześniej wykonać wstępne przetwarzanie w związku?
David Weiser,
1
@David. Nie oczekuje się wstępnego przetwarzania.
Michael
3
Czy w tablicach dozwolone są duplikaty?
David Weiser,

Odpowiedzi:

48

Masz to, po prostu kontynuuj! I uważaj na indeksy ...

Aby trochę uprościć, przyjmuję, że N i M są> k, więc złożoność tutaj wynosi O (log k), czyli O (log N + log M).

Pseudo kod:

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]

Do demonstracji możesz użyć niezmiennika pętli i + j = k, ale nie odrobię całej twojej pracy domowej :)

Jules Olléon
źródło
14
To nie jest prawdziwy dowód, ale idea algorytmu polega na tym, że utrzymujemy i + j = k i znajdujemy takie i i j, aby a [i-1] <b [j-1] <a [i] ( lub na odwrót). Ponieważ w „a” są elementy i mniejsze niż b [j-1], a elementy j-1 w „b” mniejsze niż b [j-1], b [j-1] to i + j-1 + 1 = k-ty najmniejszy element. Aby znaleźć takie i, j algorytm przeprowadza przeszukiwanie dychotomiczne na tablicach. Ma sens?
Jules Olléon,
8
Dlaczego O (log k) jest O (log n + log m)?
Rajendra Uppal
7
To nie działa, jeśli wszystkie wartości w tablicy 1 występują przed wartościami w tablicy 2.
John Kurlak
3
Dlaczego na początku używałeś k / 4 jako kroku?
Maggie,
2
Jak wspomniał @JohnKurlak, nie działa dla wartości, w których całe a jest mniejsze niż b, patrz repl.it/HMYf/0
Jeremy S.
69

Mam nadzieję, że nie odpowiadam na twoje zadanie domowe, ponieważ minął ponad rok od czasu zadania tego pytania. Oto rekurencyjne rozwiązanie ogonowe, które zajmie log (len (a) + len (b)) czas.

Założenie: dane wejściowe są prawidłowe. tj. k należy do zakresu [0, len (a) + len (b)]

Przypadki podstawowe:

  • Jeśli długość jednej z tablic wynosi 0, odpowiedzią jest k-ty element drugiej tablicy.

Kroki redukcji:

  • Jeśli indeks mid a+ mid index bjest mniejszy niżk
    • Jeśli środkowy element ajest większy niż środkowy element b, możemy zignorować pierwszą połowę b, dostosuj k.
    • w przeciwnym razie zignoruj ​​pierwszą połowę a, dostosuj k.
  • W przeciwnym razie, jeśli kjest mniejsza niż suma średnich indeksów ai b:
    • Jeśli środkowy element ajest większy niż środkowy element b, możemy bezpiecznie zignorować drugą połowęa
    • inaczej możemy zignorować drugą połowę b

Kod:

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)

Zwróć uwagę, że moim rozwiązaniem jest tworzenie nowych kopii mniejszych tablic w każdym wywołaniu, można to łatwo wyeliminować, przekazując tylko indeksy początku i końca oryginalnych tablic.

lambdapilgrim
źródło
4
dlaczego nazywasz to kthlargest()zwraca (k+1)-ty najmniejszy element, np. 1jest drugim najmniejszym elementem w 0,1,2,3ie, funkcja zwraca sorted(a+b)[k].
jfs
2
Przekonwertowałem twój kod na C ++ . Wydaje się, że działa
jfs
1
czy mógłbyś wyjaśnić, dlaczego ważne jest porównywanie sumy średnich indeksów a i b z k?
Maggie,
3
W krokach redukcji ważne jest, aby pozbyć się wielu elementów w jednej z tablic proporcjonalnych do jej długości, aby uzyskać logarytmiczny czas wykonywania. (Tutaj pozbywamy się połowy). Aby to zrobić, musimy wybrać jedną tablicę, której jedną z połówek możemy bezpiecznie zignorować. Jak to zrobimy? Pewnie eliminując połowę, o której wiemy, że na pewno nie będzie k-tego elementu.
lambdapilgrim
1
Porównanie k z sumą połówkowych długości tablic daje nam informacje o tym, która połowa jednej z tablic może zostać wyeliminowana. Jeśli k jest większe niż suma połówek długości, wiemy, że można wyeliminować pierwszą połowę jednej z tablic. Odwrotnie, jeśli k jest mniejsze. Zauważ, że nie możemy od razu wyeliminować połowy z każdej tablicy. Aby zdecydować, którą połowę tablicy wyeliminować, wykorzystujemy fakt, że obie tablice są posortowane, więc jeśli k jest większe niż suma połówek długości, możemy wyeliminować pierwszą połowę tablicy, której środkowy element jest mniejszy z dwa środkowe elementy. Nawzajem.
lambdapilgrim
34

Wiele osób odpowiedziało na to pytanie „k-ty najmniejszy element z dwóch posortowanych tablic”, ale zwykle zawierało tylko ogólne pomysły, a nie jasny działający kod lub analizę warunków brzegowych.

Tutaj chciałbym szczegółowo go rozwinąć, uwzględniając sposób, w jaki poszedłem, aby pomóc niektórym nowicjuszom zrozumieć, z moim poprawnym działającym kodem Java. A1i A2są dwiema sortowanymi rosnącymi tablicami, odpowiednio z size1i size2jako długość. Musimy znaleźć k-ty najmniejszy element z sumy tych dwóch tablic. Tutaj rozsądnie zakładamy, że to (k > 0 && k <= size1 + size2)oznacza, że A1iA2 nie może być jednocześnie puste.

Najpierw podejdźmy do tego pytania za pomocą powolnego algorytmu O (k). Metoda polega na porównaniu pierwszego elementu zarówno tablicy, jak A1[0]i A2[0]. Weź mniejszą, powiedzmy A1[0]do naszej kieszeni. Następnie porównaj A1[1]z A2[0]i tak dalej. Powtarzaj tę czynność, aż nasza kieszeń osiągnie kelementy. Bardzo ważne: w pierwszym kroku możemy zobowiązać się tylko do A1[0]kieszeni. Nie możemy uwzględniać ani wykluczać A2[0]!!!

Poniższy kod O (k) daje jeden element przed poprawną odpowiedzią. Tutaj używam go, aby pokazać mój pomysł i warunek brzegowy analizy. Mam poprawny kod po tym:

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];
    }
}

Najpotężniejszym pomysłem jest to, że w każdej pętli zawsze używamy podejścia opartego na przypadku podstawowym. Po zatwierdzeniu aktualnego najmniejszego elementu zbliżamy się o krok do celu: k-tego najmniejszego elementu. Nigdy nie wskakuj w sam środek i nie daj się zagubić!

Przestrzegając powyższego przypadku bazowego kodu k == 1, k == size1+size2i łącząc z tym A1iA2 nie można oba być puste. Możemy przekształcić tę logikę w bardziej zwięzły styl.

Oto powolny, ale poprawny działający kod:

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];
    }
}

Teraz możemy wypróbować szybszy algorytm działający na O (log k). Podobnie porównaj A1[k/2]z A2[k/2]; jeśli A1[k/2]jest mniejszy, to wszystkie elementy od A1[0]do A1[k/2]powinny znajdować się w naszej kieszeni. Chodzi o to, aby nie zatwierdzać tylko jednego elementu w każdej pętli; pierwszy krok zawiera k/2elementy. Ponownie, nie możemy włączyć lub wyłączyć A2[0], aby A2[k/2]w każdym razie. Więc w pierwszym kroku nie możemy przejść dalej niż k/2elementy. W drugim kroku nie możemy przejść dalejk/4 elementy ...

Po każdym kroku zbliżamy się znacznie do k-tego elementu. W tym samym czasie każdy krok staje się coraz mniejszy, aż do osiągnięcia (step == 1), czyli(k-1 == index1+index2) . Następnie możemy ponownie odwołać się do prostego i potężnego przypadku podstawowego.

Oto działający poprawny kod:

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];
    }
}

Niektórzy ludzie mogą się martwić, a co jeśli (index1+index2)przeskoczy nad k-1? Czy moglibyśmy przegapić przypadek podstawowy(k-1 == index1+index2) ? To niemożliwe. Możesz dodać 0,5 + 0,25 + 0,125 ... i nigdy nie przekroczysz 1.

Oczywiście bardzo łatwo jest przekształcić powyższy kod w algorytm rekurencyjny:

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);
}

Mam nadzieję, że powyższa analiza i kod Java pomogą ci to zrozumieć. Ale nigdy nie kopiuj mojego kodu jako pracy domowej! Twoje zdrowie ;)

Fei
źródło
1
Dziękuję bardzo za wspaniałe wyjaśnienia i odpowiedź, +1 :)
Hengameh
W pierwszym kodzie nie powinno być else if (A1[size1 - 1].compareTo(A2[size2 - 1]) < 0) zamiast else if (A1[size1 - 1].compareTo(A2[size2 - 1]) > 0)? (W kodzie kthSmallestSlowWithFault)
Hengameh
Dziękuję @Fei. Świetne wyjaśnienie. To zdumiewające, jak wiele błędnych odpowiedzi krąży w Internecie dotyczących tego problemu. Jeszcze bardziej zdumiewające jest to, że przyjęta przez nią odpowiedź na tak pytanie jest zawsze zła. Wygląda na to, że nikomu nie zależy na sprawdzaniu odpowiedzi.
Kapitan Fogetti
Może odciąć rozwiązanie O (k) po kilku krokach (powiedział 15), ponieważ zakres kroków spada dość szybko.
Sky
1
W żadnym z wywołań rekurencyjnych rozmiary A1 lub A2 nie są zmniejszane.
Aditya Joshee
5

Oto iteracyjna wersja rozwiązania @ lambdapilgrim w C ++ (zobacz wyjaśnienie algorytmu):

#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);
    }
  }
}

Działa dla wszystkich 0 <= n < (size(a) + size(b))indeksów i ma O(log(size(a)) + log(size(b)))złożoność.

Przykład

#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;
}
jfs
źródło
4

Moja próba dla pierwszych k liczb, k-tej liczby w 2 posortowanych tablicach i n posortowanych tablicach:

// 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);

Kompletny kod z narzędziami do debugowania można znaleźć pod adresem : https://github.com/brainclone/teasers/tree/master/kth

Qichao Dong
źródło
3

Oto mój kod oparty na rozwiązaniu 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;  
}  
wspaniały
źródło
1
W niektórych przypadkach to nie zadziała. Na przykład int a2 [] = {1, 2, 3, 4, 5}; int a1 [] = {5,6,8,10,12}; getNth (a1, a2, 7). Indeks tablicy wyjdzie poza granice.
Jay
2

Oto moja implementacja w C, możesz odwołać się do wyjaśnień @Julesa Olléona dla algorytmu: idea algorytmu polega na tym, że utrzymujemy i + j = k i znajdujemy takie i i j, aby a [i-1] <b [j-1] <a [i] (lub odwrotnie). Ponieważ w „a” są elementy i mniejsze niż b [j-1], a elementy j-1 w „b” mniejsze niż b [j-1], b [j-1] to i + j-1 + 1 = k-ty najmniejszy element. Aby znaleźć takie i, j algorytm przeprowadza przeszukiwanie dychotomiczne na tablicach.

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);
   }
 }
nie jest zły
źródło
2

Oto moje rozwiązanie. Kod C ++ drukuje k-tą najmniejszą wartość, a także liczbę iteracji, aby uzyskać k-tą najmniejszą wartość za pomocą pętli, która moim zdaniem jest rzędu log (k). Kod wymaga jednak, aby k było mniejsze niż długość pierwszej tablicy, co jest ograniczeniem.

#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;
    }
}
Karthikeyan Sv
źródło
1

Pierwszy pseudokod podany powyżej nie działa dla wielu wartości. Na przykład tutaj są dwie tablice. int [] a = {1, 5, 6, 8, 9, 11, 15, 17, 19}; int [] b = {4, 7, 8, 13, 15, 18, 20, 24, 26};

To nie zadziałało dla k = 3 i k = 9 w nim. Mam inne rozwiązanie. Jest to podane poniżej.

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);
}

}

Ale ... to też nie działa dla k = 5. Istnieje parzysty / nieparzysty połów k, który nie pozwala, aby było to proste.

sn.anurag
źródło
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]);
        }
    }
}
Hrishikesh Mishra
źródło
1

Oto moje rozwiązanie w Javie. Spróbuję go dalej zoptymalizować

  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;
        }

    }
}

To jest zainspirowane Algo na wspaniałym filmie na youtube

M Sach
źródło
1

Link do złożoności kodu (log (n) + log (m))

Link do kodu (log (n) * log (m))

Implementacja rozwiązania (log (n) + log (m))

Chciałbym dodać swoje wyjaśnienie do problemu. Jest to klasyczny problem, w którym musimy wykorzystać fakt, że dwie tablice są posortowane. otrzymaliśmy dwie posortowane tablice arr1 o rozmiarze sz1 i arr2 o rozmiarze sz2

a) Załóżmy, że jeśli

Sprawdzanie, czy k jest poprawne

k jest> (sz1 + sz2)

wtedy nie możemy znaleźć k-tego najmniejszego elementu w unii obu posortowanych tablic rytm So return Invalid data. b) Teraz, jeśli powyższy warunek okaże się fałszywy i mamy ważną i wykonalną wartość k,

Zarządzanie przypadkami brzegowymi

Dodamy obie tablice przez wartości -infinity na początku i + wartości nieskończoności na końcu, aby pokryć skrajne przypadki k = 1,2 i k = (sz1 + sz2-1), (sz1 + sz2) itd.

Teraz obie tablice mają wielkość (SZ1 + 2) i (SZ2 + 2) , odpowiednio

Główny algorytm

Teraz wykonamy wyszukiwanie binarne na arr1. Wyszukamy binarnie na arr1, szukając indeksu i, startIndex <= i <= endIndex

takie, że jeśli znajdziemy odpowiedni indeks j w arr2 używając ograniczenia {(i + j) = k}, to jeśli

jeśli (arr2 [j-1] <arr1 [i] <arr2 [j]) , to arr1 [i] jest k-tym najmniejszym (przypadek 1)

else if (arr1 [i-1] <arr2 [j] <arr1 [i]) , to arr2 [i] jest k-tym najmniejszym (przypadek 2)

else oznacza arr1 [i] <arr2 [j-1] <arr2 [j] (przypadek3)

lub arr2 [j-1] <arr2 [j] <arr1 [i] (Case4)

Ponieważ wiemy, że k-ty najmniejszy element ma (k-1) elementów mniejszych od niego w połączeniu obu tablic ryt? Więc,

W przypadku 1 , co zrobiliśmy, upewniliśmy się, że istnieje łącznie (k-1) mniejszych elementów do arr1 [i], ponieważ elementy mniejsze niż arr1 [i] w tablicy arr1 mają liczbę i-1 niż znamy (arr2 [ j-1] <arr1 [i] <arr2 [j]), a liczba elementów mniejsza niż arr1 [i] w arr2 wynosi j-1, ponieważ j jest znalezione przy użyciu (i-1) + (j-1) = (k -1) Więc k-tym najmniejszym elementem będzie arr1 [i]

Ale odpowiedź nie zawsze może pochodzić z pierwszej tablicy, tj. Arr1, więc sprawdziliśmy przypadek2, który również spełnia podobnie jak przypadek 1, ponieważ (i-1) + (j-1) = (k-1). Teraz, jeśli mamy (arr1 [i-1] <arr2 [j] <arr1 [i]), mamy w sumie k-1 elementów mniejszych niż arr2 [j] w połączeniu obu tablic, więc jest to k-ty najmniejszy element.

W przypadku 3 , aby przekształcić go w dowolny przypadek 1 lub 2, musimy zwiększyć i i j zostanie znalezione zgodnie z ograniczeniem {(i + j) = k} tj. W wyszukiwaniu binarnym przejdź do prawej części, tj. Make startIndex = middleIndex

W przypadku 4 , aby przekształcić go w którykolwiek z przypadków 1 lub 2, musimy zmniejszyć i oraz j zostanie znalezione zgodnie z ograniczeniem {(i + j) = k} tj. W wyszukiwaniu binarnym przejdź do lewej części, czyli make endIndex = middleIndex .

Teraz, jak zdecydować startIndex i endIndex na początku wyszukiwania binarnego po arr1 z startindex = 1 i endIndex = ??. Musimy zdecydować.

Jeśli k> sz1, endIndex = (sz1 + 1), w przeciwnym razie endIndex = k;

Ponieważ jeśli k jest większe niż rozmiar pierwszej tablicy, być może będziemy musieli przeprowadzić przeszukiwanie binarne w całej tablicy arr1, w przeciwnym razie musimy wziąć tylko pierwsze k elementów, ponieważ sz1-k elementów nigdy nie przyczyni się do obliczenia k-tego najmniejszego.

KOD pokazano poniżej

// 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;

}

Rozwiązanie złożoności (log (n) * log (m))

Po prostu przegapiłem korzyść z faktu, że dla każdego i j można znaleźć za pomocą ograniczenia {(i-1) + (j-1) = (k-1)} Więc dla każdego ii dalej stosowałem wyszukiwanie binarne na drugiej tablicy znaleźć j takie, że arr2 [j] <= arr1 [i]. Więc to rozwiązanie można dalej zoptymalizować

Vinayak Sangar
źródło
1

Zasadniczo, dzięki temu podejściu możesz odrzucić k / 2 elementów na każdym kroku. K będzie się zmieniać rekurencyjnie od k => k / 2 => k / 4 => ... aż osiągnie 1. Tak więc złożoność czasowa wynosi O (logk)

Przy k = 1 otrzymujemy najniższą z dwóch tablic.

Poniższy kod jest w JAVA. Zwróć uwagę, że w kodzie odejmujemy 1 (-1) od indeksów, ponieważ indeks tablicy Java zaczyna się od 0, a nie 1, np. k = 3 jest reprezentowane przez element w drugim indeksie tablicy.

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));
    }
}
FaaduBaalak
źródło
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;
}

Złożoność czasowa wynosi O (log (min (n, m)))

HeadAndTail
źródło
1

Większość odpowiedzi, które tu znalazłem, dotyczy obu tablic. chociaż jest dobry, ale trudniej go wdrożyć, ponieważ istnieje wiele skrajnych przypadków, którymi musimy się zająć. Poza tym większość implementacji jest rekurencyjna, co zwiększa złożoność przestrzeni stosu rekurencji. Więc zamiast skupiać się na obu tablicach, zdecydowałem się po prostu skupić się na mniejszej tablicy i przeprowadzić wyszukiwanie binarne tylko na mniejszej tablicy i dostosować wskaźnik do drugiej tablicy na podstawie wartości wskaźnika w pierwszej tablicy. Dzięki poniższej implementacji mamy złożoność O(log(min(n,m))ze O(1)złożonością przestrzeni.

    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");
}

Mamy zakres [low, high]for array ai zawężamy ten zakres, przechodząc dalej przez algorytm. sizeApokazuje, ile elementów z kelementów pochodzi z tablicy ai pochodzi z wartości lowi high.sizeBto ta sama definicja, z wyjątkiem tego, że obliczamy wartość w ten sposób sizeA+sizeB=k. Opierając się na wartościach na tych dwóch granicach, stwierdzamy, że musimy rozciągnąć do prawej strony w tablicy alub zmniejszyć do lewej strony. jeśli utknęliśmy w tej samej pozycji, oznacza to, że znaleźliśmy rozwiązanie i zwrócimy maksimum wartości z pozycji sizeA-1od ai sizeB-1od b.

Arnold
źródło
0

Sprawdź ten kod.

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();
Anantha Krishnan
źródło
Podaj wyjaśnienie
Abhijit Sarkar
0

Poniżej kodu C #, aby znaleźć k-ten najmniejszy element w Unii dwóch posortowanych tablic. Złożoność czasowa: 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);

            }
        }
Piyush Patel
źródło
nie ma błędu, przetestowałem mój kod, zanim wysłałem go do SO
Piyush Patel
1
Dzięki sammy333, zaktualizowałem kod. teraz działa
Piyush Patel
(Nie obliczaj midAz endAif k < n. Sprawdź krótkie tablice, zaczynając od return B[startB + k - 1];.)
Greybeard