Wie finde ich das k-te kleinste Element in der Vereinigung zweier sortierter Arrays?


106

Dies ist eine Hausaufgabenfrage. Sie sagen, es dauert O(logN + logM)wo Nund Msind die Arrays Längen.

Nennen wir die Arrays aund b. Offensichtlich können wir alle ignorieren a[i]und b[i]wo i> k.
Vergleichen wir zuerst a[k/2]und b[k/2]. Lassen Sie b[k/2]> a[k/2]. Daher können wir auch alle verwerfen b[i], wobei i> k / 2 ist.

Jetzt haben wir alle a[i], wo i <k und alle b[i], wo i <k / 2, um die Antwort zu finden.

Was ist der nächste Schritt?


6
Waren alle diese Schritte in der Zuweisung enthalten oder sind die obigen Schritte der Beginn Ihres Algorithmus?
Kendrick

18
Die obigen Schritte sind meine.
Michael

Bezieht man sich O(logN + logM)nur auf die Zeit, die benötigt wird, um das k-te Element zu finden? Kann die Gewerkschaft vorher vorverarbeitet werden?
David Weiser

1
@David. Es wird keine Vorverarbeitung erwartet.
Michael

3
Sind Duplikate in den Arrays zulässig?
David Weiser

Antworten:


48

Du hast es, mach einfach weiter! Und seien Sie vorsichtig mit den Indizes ...

Zur Vereinfachung gehe ich davon aus, dass N und M> k sind, daher ist die Komplexität hier O (log k), also O (log N + log M).

Pseudocode:

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]

Für die Demonstration können Sie die Schleifeninvariante i + j = k verwenden, aber ich werde nicht alle Ihre Hausaufgaben machen :)


14
Dies ist kein wirklicher Beweis, aber die Idee hinter dem Algorithmus ist, dass wir i + j = k beibehalten und solche i und j finden, so dass a [i-1] <b [j-1] <a [i] ( oder umgekehrt). Da es nun i Elemente in 'a' gibt, die kleiner als b [j-1] sind, und j-1 Elemente in 'b', die kleiner als b [j-1] sind, ist b [j-1] das i + j-1 + 1 = kth kleinstes Element. Um ein solches i, j zu finden, führt der Algorithmus eine dichotomische Suche in den Arrays durch. Macht Sinn?
Jules Olléon

8
Wie kommt es, dass O (log k) O (log n + log m) ist?
Rajendra Uppal

7
Dies funktioniert nicht, wenn alle Werte in Array 1 vor den Werten in Array 2 liegen.
John Kurlak

3
Warum haben Sie k / 4 zuerst als Schritt verwendet?
Maggie

2
Wie @JohnKurlak erwähnte, funktioniert es nicht für Werte, bei denen das ganze a kleiner als b ist, siehe repl.it/HMYf/0
Jeremy S.

69

Ich hoffe, ich beantworte Ihre Hausaufgaben nicht, da diese Frage vor über einem Jahr gestellt wurde. Hier ist eine rekursive Endlösung, die log (len (a) + len (b)) Zeit benötigt.

Annahme: Die Eingänge sind richtig. dh k liegt im Bereich [0, len (a) + len (b)]

Basisfälle:

  • Wenn die Länge eines der Arrays 0 ist, ist die Antwort das k-te Element des zweiten Arrays.

Reduktionsschritte:

  • Wenn der mittlere Index von a+ mittlerer Index von bkleiner als istk
    • Wenn das mittlere Element von agrößer als das mittlere Element von ist b, können wir die erste Hälfte von ignorieren bund anpassen k.
    • Andernfalls ignorieren Sie die erste Hälfte von a, passen Sie an k.
  • Andernfalls ist if kkleiner als die Summe der Mittelindizes von aund b:
    • Wenn das mittlere Element von agrößer als das mittlere Element von ist b, können wir die zweite Hälfte von ignorierena
    • sonst können wir die zweite Hälfte von ignorieren b

Code:

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)

Bitte beachten Sie, dass meine Lösung darin besteht, bei jedem Aufruf neue Kopien kleinerer Arrays zu erstellen. Dies kann leicht beseitigt werden, indem nur Start- und Endindizes für die ursprünglichen Arrays übergeben werden.


4
Warum nennst du es, kthlargest()es gibt (k+1)-th kleinste Elemente zurück, z. B. 1ist es das zweitkleinste Element in 0,1,2,3dh deine Funktion gibt zurück sorted(a+b)[k].
JFS

2
Ich habe Ihren Code in C ++ konvertiert . Es scheint zu funktionieren
jfs

1
Könnten Sie bitte erklären, warum es wichtig ist, die Summe der mittleren Indizes von a und b mit k zu vergleichen?
Maggie

3
In den Reduktionsschritten ist es wichtig, eine Anzahl von Elementen in einem der Arrays proportional zu seiner Länge zu entfernen, um die Laufzeit logarithmisch zu machen. (Hier werden wir die Hälfte los). Dazu müssen wir ein Array auswählen, dessen eine der Hälften wir ignorieren können. Wie machen wir das? Wenn wir die Hälfte sicher eliminieren, wissen wir sicher, dass wir nicht das k-te Element haben werden.
Lambdapilgrim

1
Der Vergleich von k mit der Summe der halben Längen der Arrays gibt uns Auskunft darüber, welche Hälfte eines der Arrays eliminiert werden kann. Wenn k größer als die Summe der halben Längen ist, wissen wir, dass die erste Hälfte eines der Arrays eliminiert werden kann. Gegenüber, wenn k kleiner ist. Beachten Sie, dass wir nicht eine Hälfte von jedem Array gleichzeitig entfernen können. Um zu entscheiden, welche Hälfte von welchem ​​Array eliminiert werden soll, nutzen wir die Tatsache, dass beide Arrays sortiert sind. Wenn also k größer als die Summe der halben Längen ist, können wir die erste Hälfte des Arrays eliminieren, dessen mittleres Element das kleinere von ist zwei mittlere Elemente. Und umgekehrt.
Lambdapilgrim

34

Viele Leute beantworteten diese Frage "kth kleinstes Element aus zwei sortierten Arrays", aber normalerweise nur mit allgemeinen Ideen, nicht mit einem klaren Arbeitscode oder einer Randbedingungsanalyse.

Hier möchte ich es sorgfältig ausarbeiten, um einigen Anfängern das Verständnis mit meinem korrekt funktionierenden Java-Code zu erleichtern. A1und A2sind zwei sortierte aufsteigende Arrays mit size1bzw. size2als Länge. Wir müssen das k-te kleinste Element aus der Vereinigung dieser beiden Arrays finden. Hier nehmen wir das vernünftigerweise an (k > 0 && k <= size1 + size2), was dies impliziert A1und A2nicht beide leer sein kann.

Lassen Sie uns diese Frage zunächst mit einem langsamen O (k) -Algorithmus angehen. Die Methode besteht darin, das erste Element beider Arrays A1[0]und zu vergleichen A2[0]. Nehmen Sie den kleineren, sagen Sie A1[0]weg in unsere Tasche. Dann vergleiche A1[1]mit A2[0]und so weiter. Wiederholen Sie diesen Vorgang, bis unsere Tasche kElemente erreicht hat . Sehr wichtig: Im ersten Schritt können wir uns nur A1[0]in unserer Tasche festlegen. Wir können NICHT einschließen oder ausschließen A2[0]!!!

Der folgende O (k) -Code gibt Ihnen ein Element vor der richtigen Antwort. Hier zeige ich damit meine Idee und analysiere die Randbedingungen. Ich habe den richtigen Code nach diesem:

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

Die mächtigste Idee ist, dass wir in jeder Schleife immer den Basisfallansatz verwenden. Nachdem wir uns auf das aktuell kleinste Element festgelegt haben, kommen wir dem Ziel einen Schritt näher: dem k-ten kleinsten Element. Springe niemals in die Mitte und mache dich verwirrt und verloren!

Den obigen Code Basisfall Durch die Beobachtung k == 1, k == size1+size2, und damit kombinieren A1und A2können nicht beide leer sein. Wir können die Logik in einen prägnanteren Stil umwandeln.

Hier ist ein langsamer, aber korrekter Arbeitscode:

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

Jetzt können wir versuchen, einen schnelleren Algorithmus bei O (log k) auszuführen. Vergleichen Sie in ähnlicher Weise A1[k/2]mit A2[k/2]; Wenn A1[k/2]es kleiner ist, sollten alle Elemente von A1[0]bis A1[k/2]in unserer Tasche sein. Die Idee ist, nicht nur ein Element in jeder Schleife festzulegen. Der erste Schritt enthält k/2Elemente. Auch hier können wir nicht einschließen oder ausschließen A2[0]zu A2[k/2]sowieso. Im ersten Schritt können wir also nicht mehr als k/2Elemente verwenden. Für den zweiten Schritt können wir nicht mehr als k/4Elemente gehen ...

Nach jedem Schritt kommen wir dem k-ten Element viel näher. Gleichzeitig wird jeder Schritt immer kleiner, bis wir erreichen (step == 1), was ist (k-1 == index1+index2). Dann können wir uns wieder auf den einfachen und leistungsstarken Basisfall beziehen.

Hier ist der korrekte Arbeitscode:

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

Einige Leute könnten sich Sorgen machen, wenn sie (index1+index2)über k-1 springen? Könnten wir den Basisfall verpassen (k-1 == index1+index2)? Das ist nicht möglich. Sie können 0,5 + 0,25 + 0,125 addieren ... und Sie werden niemals über 1 hinausgehen.

Natürlich ist es sehr einfach, den obigen Code in einen rekursiven Algorithmus umzuwandeln:

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

Ich hoffe, die obige Analyse und der Java-Code könnten Ihnen beim Verständnis helfen. Aber kopiere niemals meinen Code als deine Hausaufgabe! Prost ;)


1
Vielen Dank für Ihre tollen Erklärungen und Antworten, +1 :)
Hengameh

Sollte im ersten Code nicht else if (A1[size1 - 1].compareTo(A2[size2 - 1]) < 0) statt sein else if (A1[size1 - 1].compareTo(A2[size2 - 1]) > 0)? (In kthSmallestSlowWithFault-Code)
Hengameh

Danke @Fei. Tolle Erklärung. Es ist erstaunlich, wie viele falsche Antworten zu diesem Problem über das Internet verbreitet werden. Es ist noch erstaunlicher, dass die akzeptierte Antwort auf SO bezüglich dieser Frage immer die falsche ist. Es scheint, als würde niemand die Antworten testen wollen.
Kapitän Fogetti

Möglicherweise wird die O (k) -Lösung nach einigen Schritten unterbrochen (siehe 15), da der Schrittbereich ziemlich schnell abnimmt.
Sky

1
In keinem der rekursiven Aufrufe werden die Größen von A1 oder A2 reduziert.
Aditya Joshee

5

Hier ist eine iterative C ++ - Version der Lösung von @ lambdapilgrim (siehe Erläuterung des dortigen Algorithmus):

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

Es funktioniert für alle 0 <= n < (size(a) + size(b))Indizes und ist O(log(size(a)) + log(size(b)))komplex.

Beispiel

#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

Mein Versuch für die ersten k Zahlen, die k-te Zahl in 2 sortierten Arrays und in n sortierten Arrays:

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

Den vollständigen Code mit Debug-Utils finden Sie unter: https://github.com/brainclone/teasers/tree/master/kth


3

Hier ist mein Code, der auf Jules Olleons Lösung basiert:

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
Dies funktioniert in einigen Fällen nicht. Zum Beispiel ist int a2 [] = {1,2,3,4, 5}; int a1 [] = {5,6,8,10,12}; getNth (a1, a2, 7). Der Index des Arrays überschreitet die Grenze.
Jay

2

Hier ist meine Implementierung in C, Sie können sich für den Algorithmus auf die Erklärungen von @Jules Olléon beziehen: Die Idee hinter dem Algorithmus ist, dass wir i + j = k beibehalten und solche i und j finden, so dass a [i-1] <b [j-1] <a [i] (oder umgekehrt). Da es nun i Elemente in 'a' gibt, die kleiner als b [j-1] sind, und j-1 Elemente in 'b', die kleiner als b [j-1] sind, ist b [j-1] das i + j-1 + 1 = kth kleinstes Element. Um ein solches i, j zu finden, führt der Algorithmus eine dichotomische Suche in den Arrays durch.

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

Hier ist meine Lösung. Der C ++ - Code gibt den k-ten kleinsten Wert sowie die Anzahl der Iterationen aus, um den k-ten kleinsten Wert mithilfe einer Schleife zu erhalten, die meiner Meinung nach in der Reihenfolge log (k) liegt. Der Code erfordert jedoch, dass k kleiner als die Länge des ersten Arrays ist, was eine Einschränkung darstellt.

#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

Der oben angegebene erste Pseudocode funktioniert für viele Werte nicht. Hier sind zum Beispiel zwei Arrays. int [] a = {1, 5, 6, 8, 9, 11, 15, 17, 19}; int [] b = {4, 7, 8, 13, 15, 18, 20, 24, 26};

Es funktionierte nicht für k = 3 und k = 9 darin. Ich habe eine andere Lösung. Es ist unten angegeben.

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

}

Aber ... es funktioniert auch nicht für k = 5. Es gibt diesen geraden / ungeraden Fang von k, der es nicht einfach sein lässt.


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

Hier ist meine Lösung in Java. Ich werde versuchen, es weiter zu optimieren

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

    }
}

Dies ist inspiriert von Algo bei einem wunderbaren Youtube-Video


1

Link zur Codekomplexität (log (n) + log (m))

Link zum Code (log (n) * log (m))

Implementierung der Lösung (log (n) + log (m))

Ich möchte dem Problem meine Erklärung hinzufügen. Dies ist ein klassisches Problem, bei dem wir die Tatsache nutzen müssen, dass die beiden Arrays sortiert sind. Wir haben zwei sortierte Arrays arr1 der Größe sz1 und arr2 der Größe sz2 erhalten

a) Nehmen wir an, wenn

Überprüfen, ob k gültig ist

k ist> (sz1 + sz2)

dann können wir das k-te kleinste Element in der Vereinigung beider sortierter Arrays nicht finden. Geben Sie also ungültige Daten zurück. b) Wenn nun die obige Bedingung falsch ist und wir einen gültigen und realisierbaren Wert von k haben,

Edge Cases verwalten

Wir werden sowohl die Arrays durch -Infinity-Werte an der Vorderseite als auch + Infinity-Werte am Ende anhängen, um die Randfälle von k = 1,2 und k = (sz1 + sz2-1), (sz1 + sz2) usw. abzudecken.

Nun sowohl die Arrays haben eine Größe (SZ1 + 2) und (SZ2 + 2) jeweils

Hauptalgorithmus

Jetzt führen wir eine binäre Suche für arr1 durch. Wir führen eine binäre Suche für arr1 durch und suchen nach einem Index i, startIndex <= i <= endIndex

so dass, wenn wir den entsprechenden Index j in arr2 unter Verwendung der Bedingung {(i + j) = k} finden, dann wenn

wenn (arr2 [j-1] <arr1 [i] <arr2 [j]) , dann ist arr1 [i] der k-te kleinste (Fall 1)

sonst, wenn (arr1 [i-1] <arr2 [j] <arr1 [i]) , dann ist arr2 [i] der k-te kleinste (Fall 2)

sonst bedeutet entweder arr1 [i] <arr2 [j-1] <arr2 [j] (Fall3)

oder arr2 [j-1] <arr2 [j] <arr1 [i] (Fall 4)

Da wir wissen, dass das k-te kleinste Element (k-1) Elemente hat, die kleiner sind als es in Vereinigung beider Arrays ryt? So,

In Fall 1 haben wir sichergestellt, dass arr1 [i] insgesamt (k-1) kleinere Elemente enthält, da Elemente, die kleiner als arr1 [i] im Array arr1 sind, eine Anzahl von i-1 haben, als wir wissen (arr2 [ j-1] <arr1 [i] <arr2 [j]) und die Anzahl der Elemente, die in arr2 kleiner als arr1 [i] sind, ist j-1, da j unter Verwendung von (i-1) + (j-1) = (k) gefunden wird -1) Das k-te kleinste Element ist also arr1 [i]

Die Antwort kommt jedoch möglicherweise nicht immer vom ersten Array, dh arr1, und wir haben nach Fall2 gesucht, der ähnlich wie Fall 1 erfüllt, weil (i-1) + (j-1) = (k-1). Wenn wir nun (arr1 [i-1] <arr2 [j] <arr1 [i]) haben, haben wir insgesamt k-1 Elemente, die kleiner als arr2 [j] sind, in Vereinigung beider Arrays, so dass es das k-te kleinste Element ist.

In case3 , es zu einem Fall zu bilden , 1 oder Fall 2, müssen wir i inkrementieren und j wird entsprechend ermittelt werden constraint mit {(i + j) = k} dh in Binärsuche Bewegung zu rechten Teil dh machen start = middleIndex

In case4 , es zu einem Fall zu bilden , 1 oder Fall 2, müssen wir i dekrementieren und j wird entsprechend ermittelt wird constraint mit {(i + j) = k} dh in binärer Suche nach links bewegt Teil dh machen endIndex = middleIndex .

Nun, wie man StartIndex und EndIndex zu Beginn der binären Suche über arr1 mit startindex = 1 und endIndex = ?? entscheidet. Wir müssen uns entscheiden.

Wenn k> sz1, endIndex = (sz1 + 1), sonst endIndex = k;

Denn wenn k größer als die Größe des ersten Arrays ist, müssen wir möglicherweise eine binäre Suche über das gesamte Array arr1 durchführen, andernfalls müssen wir nur die ersten k Elemente davon nehmen, da sz1-k-Elemente niemals zur Berechnung des k-ten kleinsten beitragen können.

CODE unten gezeigt

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

}

Zur Lösung der Komplexität (log (n) * log (m))

Ich habe nur den Vorteil der Tatsache verpasst, dass für jedes i das j unter Verwendung der Einschränkung {(i-1) + (j-1) = (k-1)} gefunden werden kann. Also wurde für jedes ii die binäre Suche auf das zweite Array weiter angewendet um j so zu finden, dass arr2 [j] <= arr1 [i]. Diese Lösung kann also weiter optimiert werden


1

Grundsätzlich können Sie über diesen Ansatz bei jedem Schritt k / 2 Elemente verwerfen. Das K ändert sich rekursiv von k => k / 2 => k / 4 => ... bis es 1 erreicht. Die Zeitkomplexität ist also O (logk)

Bei k = 1 erhalten wir das niedrigste der beiden Arrays.

Der folgende Code befindet sich in JAVA. Bitte beachten Sie, dass wir 1 (-1) im Code von den Indizes subtrahieren, da der Index des Java-Arrays bei 0 beginnt und nicht bei 1, z. k = 3 wird durch das Element im 2. Index eines Arrays dargestellt.

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

Zeitkomplexität ist O (log (min (n, m)))


1

Die meisten Antworten, die ich hier gefunden habe, konzentrieren sich auf beide Arrays. Es ist zwar gut, aber schwieriger zu implementieren, da wir uns um viele Randfälle kümmern müssen. Außerdem sind die meisten Implementierungen rekursiv, was die räumliche Komplexität des Rekursionsstapels erhöht. Anstatt mich auf beide Arrays zu konzentrieren, habe ich mich entschieden, mich nur auf das kleinere Array zu konzentrieren und die binäre Suche nur auf das kleinere Array durchzuführen und den Zeiger für das zweite Array basierend auf dem Wert des Zeigers im ersten Array anzupassen. Durch die folgende Implementierung haben wir die Komplexität von O(log(min(n,m))mit O(1)Raumkomplexität.

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

Wir haben einen Bereich [low, high]für Array aund wir schränken diesen Bereich ein, wenn wir den Algorithmus weiter durchlaufen. sizeAZeigt an, wie viele Elemente von kElementen aus dem Array stammen, aund leitet sie vom Wert von lowund ab high. sizeBist die gleiche Definition, außer dass wir den Wert so berechnen, dass sizeA+sizeB=k. Die basierend auf den Werten an diesen beiden Rändern schließen daraus, dass wir uns im Array nach rechts erstrecken aoder nach links schrumpfen müssen. Wenn wir an derselben Position bleiben, bedeutet dies, dass wir die Lösung gefunden haben und das Maximum der Werte an der Position von sizeA-1von aund sizeB-1von zurückgeben b.


0

Überprüfen Sie diesen Code.

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

Erklären Sie
Abhijit Sarkar

0

Unterhalb des C # -Codes finden Sie das k-te kleinste Element in der Union zweier sortierter Arrays. Zeitkomplexität: 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);

            }
        }

Es gibt keinen Fehler, ich habe meinen Code getestet, bevor ich auf SO
Piyush Patel

1
Danke sammy333, ich habe den Code aktualisiert. Jetzt funktioniert es
Piyush Patel

(Berechnen Sie nicht midAvon endAif k < n. return B[startB + k - 1];
Suchen
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.