Come trovare il k-esimo elemento più piccolo nell'unione di due array ordinati?


106

Questa è una domanda per i compiti. Dicono che ci vuole O(logN + logM)dove NeM sono le lunghezze degli array.

Assegniamo un nome agli array ae b. Ovviamente possiamo ignorare tutto a[i]e b[i]dove i> k.
Per prima cosa confrontiamo a[k/2]e b[k/2]. Let b[k/2]> a[k/2]. Quindi possiamo scartare anche tutto b[i], dove i> k / 2.

Ora abbiamo all a[i], dove i <k e all b[i], dove i <k / 2 per trovare la risposta.

Qual'è il prossimo passo?


6
Tutti questi passaggi sono stati inclusi nell'assegnazione o i passaggi precedenti sono l'inizio del tuo algoritmo?
Kendrick

18
I passaggi sopra sono miei.
Michael

Si O(logN + logM)riferisce solo al tempo necessario per trovare il k-esimo elemento? È possibile eseguire la preelaborazione per l'unione in anticipo?
David Weiser

1
@ David. Non è prevista alcuna pre-elaborazione.
Michael

3
Sono consentiti duplicati negli array?
David Weiser

Risposte:


48

Ce l'hai, vai avanti! E fai attenzione agli indici ...

Per semplificare un po ', assumerò che N e M siano> k, quindi la complessità qui è O (log k), che è O (log N + log M).

Pseudo-codice:

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]

Per la dimostrazione puoi usare il ciclo invariante i + j = k, ma non farò tutti i tuoi compiti :)


14
Questa non è una prova reale, ma l'idea alla base dell'algoritmo è che manteniamo i + j = k, e troviamo tali i e j in modo che a [i-1] <b [j-1] <a [i] ( O viceversa). Ora poiché ci sono i elementi in 'a' più piccoli di b [j-1] e j-1 elementi in 'b' più piccoli di b [j-1], b [j-1] è i ​​+ j-1 + 1 = k-esimo elemento più piccolo. Per trovare tale i, j l'algoritmo esegue una ricerca dicotomica sugli array. Ha senso?
Jules Olléon

8
Come mai O (log k) è O (log n + log m)?
Rajendra Uppal

7
Questo non funziona se tutti i valori nell'array 1 vengono prima dei valori nell'array 2.
John Kurlak

3
Perché all'inizio hai usato k / 4 come passaggio?
Maggie

2
Come ha detto @JohnKurlak, non funziona per i valori in cui l'intero a è minore di b, vedere repl.it/HMYf/0
Jeremy S.

69

Spero di non rispondere ai tuoi compiti perché è passato più di un anno da quando è stata posta questa domanda. Ecco una soluzione ricorsiva di coda che richiederà tempo log (len (a) + len (b)).

Presupposto: gli input sono corretti. cioè k è nell'intervallo [0, len (a) + len (b)]

Casi di base:

  • Se la lunghezza di uno degli array è 0, la risposta è il k ° elemento del secondo array.

Fasi di riduzione:

  • Se l'indice medio di a+ l'indice medio di bè minore dik
    • Se l'elemento centrale di a è maggiore dell'elemento medio di b, possiamo ignorare la prima metà di b, aggiusta k.
    • altrimenti ignora la prima metà di a , aggiusta k.
  • Altrimenti se k è inferiore alla somma degli indici medi di ae b:
    • Se l'elemento centrale di a è maggiore dell'elemento medio di b, possiamo tranquillamente ignorare la seconda metà dia
    • altrimenti possiamo ignorare la seconda metà di b

Codice:

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)

Si noti che la mia soluzione è creare nuove copie di array più piccoli in ogni chiamata, questo può essere facilmente eliminato passando solo gli indici di inizio e fine sugli array originali.


4
perché lo chiami kthlargest()restituisce (k+1)-th più piccolo elemento ad esempio, 1è il secondo elemento più piccolo in 0,1,2,3cioè, la tua funzione restituisce sorted(a+b)[k].
jfs


1
potresti spiegare perché è importante confrontare la somma degli indici medi di aeb con k?
Maggie

3
Nelle fasi di riduzione, è importante eliminare un numero di elementi in uno degli array proporzionale alla sua lunghezza per rendere logaritmico il tempo di esecuzione. (Qui ci stiamo sbarazzando della metà). Per fare ciò, dobbiamo selezionare un array di cui possiamo tranquillamente ignorare una delle due metà. Come lo facciamo? Eliminando con sicurezza la metà sappiamo per certo che non avrà l'elemento k-esimo.
lambdapilgrim

1
Il confronto di k con la somma delle mezze lunghezze degli array ci fornisce informazioni su quale metà di uno degli array può essere eliminata. Se k è maggiore della somma delle mezze lunghezze, sappiamo che la prima metà di uno degli array può essere eliminata. Opposto se k è minore. Nota che non possiamo eliminare una metà da ogni array contemporaneamente. Per decidere quale metà di quale array eliminare, approfittiamo del fatto che entrambi gli array sono ordinati, quindi se k è maggiore della somma delle mezze lunghezze, possiamo eliminare la prima metà dell'array il cui elemento centrale è il più piccolo del due elementi centrali. Vice versa.
lambdapilgrim

34

Molte persone hanno risposto a questa domanda "k-esimo elemento più piccolo da due array ordinati", ma di solito solo con idee generali, non con un codice di lavoro chiaro o con un'analisi delle condizioni al contorno.

Qui mi piacerebbe elaborarlo attentamente con il modo in cui sono andato però per aiutare alcuni principianti a capire, con il mio corretto codice Java funzionante. A1e A2sono due array ascendenti ordinati, rispettivamente con size1e size2come lunghezza. Dobbiamo trovare il k-esimo elemento più piccolo dall'unione di questi due array. Qui assumiamo ragionevolmente quello (k > 0 && k <= size1 + size2), il che lo implica A1e A2non può essere vuoto.

Innanzitutto, affrontiamo questa domanda con un algoritmo O (k) lento. Il metodo consiste nel confrontare il primo elemento di entrambi gli array A1[0]e A2[0]. Prendi quello più piccolo, dì A1[0]via in tasca. Quindi confronta A1[1]con A2[0]e così via. Ripeti questa azione finché la nostra tasca non ha raggiunto gli kelementi. Molto importante: nella prima fase, possiamo impegnarci solo A1[0]in tasca. NON possiamo includere o escludere A2[0]!!!

Il seguente codice O (k) ti dà un elemento prima della risposta corretta. Qui lo uso per mostrare la mia idea e la condizione al contorno dell'analisi. Ho il codice corretto dopo questo:

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

L'idea più potente è che in ogni ciclo usiamo sempre l'approccio del caso base. Dopo aver eseguito il commit sull'elemento più piccolo corrente, ci avviciniamo di un passo all'obiettivo: il k-esimo elemento più piccolo. Non saltare mai nel mezzo e farti confondere e perdere!

Osservando il caso del codice base di cui sopra k == 1, k == size1+size2 , e combinalo con quello A1eA2 non possono essere entrambi vuoti. Possiamo trasformare la logica in uno stile più conciso di seguito.

Ecco un codice funzionante lento ma corretto:

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

Ora possiamo provare a eseguire un algoritmo più veloce su O (log k). Allo stesso modo, confronta A1[k/2]con A2[k/2]; se A1[k/2]è più piccolo, allora tutti gli elementi da A1[0]a A1[k/2]dovrebbero essere nella nostra tasca. L'idea è di non impegnarsi solo su un elemento in ogni ciclo; il primo passaggio contiene k/2elementi. Ancora una volta, NON possiamo includere o escludere A2[0]aA2[k/2] ogni caso. Quindi, nel primo passaggio, non possiamo andare oltre gli k/2elementi. Per il secondo passaggio, non possiamo andare oltre gli k/4elementi ...

Dopo ogni passaggio, ci avviciniamo molto all'elemento k-esimo. Allo stesso tempo ogni passo diventa sempre più piccolo, finché non lo raggiungiamo(step == 1) , che è (k-1 == index1+index2). Quindi possiamo fare nuovamente riferimento al caso base semplice e potente.

Ecco il codice corretto funzionante:

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

Alcune persone potrebbero preoccuparsi di cosa succederebbe se (index1+index2)saltassero su k-1? Potremmo perdere il caso base(k-1 == index1+index2) ? È impossibile. Puoi sommare 0,5 + 0,25 + 0,125 ... e non andrai mai oltre 1.

Naturalmente, è molto facile trasformare il codice sopra in algoritmo ricorsivo:

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

Spero che l'analisi di cui sopra e il codice Java possano aiutarti a capire. Ma non copiare mai il mio codice come compito a casa! Saluti ;)


1
Grazie mille per le tue ottime spiegazioni e risposta, +1 :)
Hengameh

Nel primo codice, non dovrebbe essere else if (A1[size1 - 1].compareTo(A2[size2 - 1]) < 0) invece di else if (A1[size1 - 1].compareTo(A2[size2 - 1]) > 0)? (Nel codice kthSmallestSlowWithFault)
Hengameh

Grazie @Fei. Ottima spiegazione. È sorprendente quante risposte sbagliate circolano su Internet riguardo a questo problema. È ancora più sorprendente che la risposta accettata su SO riguardo a questa domanda sia sempre quella sbagliata. Sembra che a nessuno importi di testare le risposte.
Capitano Fogetti

Forse tagliare alla soluzione O (k) dopo alcuni passaggi (detti 15), poiché l'intervallo dei passaggi diminuisce abbastanza velocemente.
Sky

1
In nessuna delle chiamate ricorsive le dimensioni di A1 o A2 vengono ridotte.
Aditya Joshee

5

Ecco una versione iterativa C ++ della soluzione di @ lambdapilgrim (vedi la spiegazione dell'algoritmo lì):

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

Funziona per tutti gli 0 <= n < (size(a) + size(b))indici e presenta O(log(size(a)) + log(size(b)))complessità.

Esempio

#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

Il mio tentativo per i primi k numeri, il k-esimo numero in 2 array ordinati e in n array ordinati:

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

Il codice completo con le utilità di debug può essere trovato su: https://github.com/brainclone/teasers/tree/master/kth


3

Ecco il mio codice basato sulla soluzione di 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
Questo non funzionerà in alcuni casi. Ad esempio, int a2 [] = {1,2,3,4, 5}; int a1 [] = {5,6,8,10,12}; getNth (a1, a2, 7). L'indice della matrice uscirà dal limite.
Jay

2

Ecco la mia implementazione in C, puoi fare riferimento alle spiegazioni di @Jules Olléon per l'algoritmo: l'idea alla base dell'algoritmo è che manteniamo i + j = k, e troviamo tali i e j in modo che a [i-1] <b [j-1] <a [i] (o viceversa). Ora poiché ci sono i elementi in 'a' più piccoli di b [j-1] e j-1 elementi in 'b' più piccoli di b [j-1], b [j-1] è i ​​+ j-1 + 1 = k-esimo elemento più piccolo. Per trovare tale i, j l'algoritmo esegue una ricerca dicotomica sugli array.

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

Ecco la mia soluzione. Il codice C ++ stampa il k-esimo valore più piccolo e il numero di iterazioni per ottenere il k-esimo valore più piccolo utilizzando un ciclo, che a mio parere è nell'ordine di log (k). Il codice tuttavia richiede che k sia inferiore alla lunghezza del primo array che è una limitazione.

#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

Il primo pseudo codice fornito sopra, non funziona per molti valori. Ad esempio, qui ci sono due array. int [] a = {1, 5, 6, 8, 9, 11, 15, 17, 19}; int [] b = {4, 7, 8, 13, 15, 18, 20, 24, 26};

Non ha funzionato per k = 3 e k = 9 in esso. Ho un'altra soluzione. È dato di seguito.

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

}

Ma ... non funziona nemmeno per k = 5. C'è questa cattura pari / dispari di k che non lascia che sia semplice.


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

Ecco la mia soluzione in java. Cercherò di ottimizzarlo ulteriormente

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

    }
}

Questo è ispirato da Algo al meraviglioso video di YouTube


1

Collegamento alla complessità del codice (log (n) + log (m))

Collegamento al codice (log (n) * log (m))

Implementazione della soluzione (log (n) + log (m))

Vorrei aggiungere la mia spiegazione al problema. Questo è un classico problema in cui dobbiamo usare il fatto che i due array sono ordinati. ci sono stati dati due array ordinati arr1 di dimensione sz1 e arr2 di dimensione sz2

a) Supponiamo che se

Verificare se k è valido

k è> (sz1 + sz2)

quindi non possiamo trovare il k-esimo elemento più piccolo nell'unione di entrambi gli array ordinati ryt Quindi restituire dati non validi. b) Ora se la condizione di cui sopra è falsa e abbiamo un valore valido e fattibile di k,

Gestione dei casi limite

Aggiungeremo entrambi gli array in base ai valori -infinity all'inizio e ai valori + infinity alla fine per coprire i casi limite di k = 1,2 e k = (sz1 + sz2-1), (sz1 + sz2) ecc.

Ora entrambe le matrici hanno dimensioni (sz1 + 2) e (SZ2 + 2) rispettivamente

Algoritmo principale

Ora, faremo una ricerca binaria su arr1. Faremo una ricerca binaria su arr1 cercando un indice i, startIndex <= i <= endIndex

tale che se troviamo il corrispondente indice j in arr2 usando il vincolo {(i + j) = k}, allora se

se (arr2 [j-1] <arr1 [i] <arr2 [j]) , allora arr1 [i] è il k-esimo più piccolo (Caso 1)

altrimenti se (arr1 [i-1] <arr2 [j] <arr1 [i]) , allora arr2 [i] è il k-esimo più piccolo (Caso 2)

altrimenti significa arr1 [i] <arr2 [j-1] <arr2 [j] (Case3)

o arr2 [j-1] <arr2 [j] <arr1 [i] (Caso4)

Dato che sappiamo che il k-esimo elemento più piccolo ha (k-1) elementi più piccoli di esso in unione di entrambi gli array ryt? Così,

Nel caso 1 , quello che abbiamo fatto, ci siamo assicurati che ci fossero un totale di (k-1) elementi più piccoli di arr1 [i] perché gli elementi più piccoli di arr1 [i] nell'array arr1 sono i-1 in numero di quanto sappiamo (arr2 [ j-1] <arr1 [i] <arr2 [j]) e il numero di elementi più piccolo di arr1 [i] in arr2 è j-1 perché j si trova usando (i-1) + (j-1) = (k -1) Quindi il k-esimo elemento più piccolo sarà arr1 [i]

Ma la risposta potrebbe non provenire sempre dal primo array, ad esempio arr1, quindi abbiamo controllato case2 che soddisfa anche il caso 1 in modo simile perché (i-1) + (j-1) = (k-1). Ora se abbiamo (arr1 [i-1] <arr2 [j] <arr1 [i]) abbiamo un totale di k-1 elementi più piccoli di arr2 [j] in unione di entrambi gli array, quindi è il k-esimo elemento più piccolo.

Nel caso 3 , per in uno qualsiasi dei casi 1 o 2, dobbiamo incrementare i e j saranno trovati in base al vincolo {(i + j) = k} cioè nella ricerca binaria spostati nella parte destra cioè fai startIndex = middleIndex

Nel caso 4 , per formarlo in uno qualsiasi dei casi 1 o 2, dobbiamo decrementare i e j sarà trovato in base al vincolo {(i + j) = k} cioè nella ricerca binaria spostati nella parte sinistra cioè fai endIndex = middleIndex .

Ora come decidere startIndex e endIndex all'inizio della ricerca binaria su arr1 con startindex = 1 e endIndex = ??. Dobbiamo decidere.

Se k> sz1, endIndex = (sz1 + 1), altrimenti endIndex = k;

Perché se k è maggiore della dimensione del primo array, potremmo dover fare una ricerca binaria sull'intero array arr1, altrimenti dobbiamo solo prendere i primi k elementi di esso perché gli elementi sz1-k non possono mai contribuire al calcolo del k-esimo più piccolo.

CODICE mostrato di seguito

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

}

Per Soluzione di complessità (log (n) * log (m))

Mi sono perso solo il vantaggio del fatto che per ogni i il j può essere trovato usando il vincolo {(i-1) + (j-1) = (k-1)} Quindi per ogni ii è stata ulteriormente applicata la ricerca binaria sul secondo array per trovare j tale che arr2 [j] <= arr1 [i]. Quindi questa soluzione può essere ulteriormente ottimizzata


1

Fondamentalmente, tramite questo approccio puoi scartare k / 2 elementi ad ogni passaggio. La K cambierà ricorsivamente da k => k / 2 => k / 4 => ... fino a raggiungere 1. Quindi, Complessità temporale è O (logk)

A k = 1, otteniamo il più basso dei due array.

Il codice seguente è in JAVA. Si noti che stiamo sottraendo 1 (-1) nel codice dagli indici perché l'indice dell'array Java inizia da 0 e non da 1, ad es. k = 3 è rappresentato dall'elemento nel 2 ° indice di un array.

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

La complessità temporale è O (log (min (n, m)))


1

La maggior parte delle risposte che ho trovato qui si concentra su entrambi gli array. mentre è buono ma è più difficile da implementare poiché ci sono molti casi limite di cui dobbiamo occuparci. Oltre a ciò, la maggior parte delle implementazioni sono ricorsive, il che aggiunge la complessità spaziale dello stack di ricorsione. Quindi, invece di concentrarmi su entrambi gli array, ho deciso di concentrarmi solo sull'array più piccolo ed eseguire la ricerca binaria solo sull'array più piccolo e regolare il puntatore per il secondo array in base al valore del puntatore nel primo Array. Con la seguente implementazione, abbiamo la complessità di O(log(min(n,m))con la complessità dello O(1)spazio.

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

Abbiamo un intervallo di [low, high]per array ae restringiamo questo intervallo man mano che avanziamo nell'algoritmo. sizeAmostra quanti elementi degli kelementi provengono dall'array ae deriva dal valore di lowe high. sizeBè la stessa definizione tranne che calcoliamo il valore in questo modo sizeA+sizeB=k. Sulla base dei valori su quei due bordi con concludere che dobbiamo estendere al lato destro nella matrice ao restringere il lato sinistro. se siamo rimasti nella stessa posizione significa che abbiamo trovato la soluzione e restituiremo il massimo dei valori nella posizione di sizeA-1da ae sizeB-1da b.


0

Controlla questo codice.

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

Fornire spiegazioni
Abhijit Sarkar

0

Di seguito il codice C # per trovare l'elemento più piccolo k-esimo nell'unione di due matrici ordinate. Complessità temporale: 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);

            }
        }

non ci sono bug, ho testato il mio codice prima di postarlo su SO
Piyush Patel

1
Grazie sammy333, ho aggiornato il codice. ora funziona
Piyush Patel

(Non calcolare midAda endAif k < n. Verificare la presenza di array brevi, che iniziano con return B[startB + k - 1];.)
greybeard
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.