İki sıralı dizinin birleşiminde k'inci en küçük eleman nasıl bulunur?


106

Bu bir ev ödevi sorusudur. O(logN + logM)Nerede olduğunu Nve Mdizilerin uzunlukları olduğunu söylüyorlar .

Dizileri adlandıralım ave b. Açıkçası her şeyi a[i]ve b[i]i> k'yi görmezden gelebiliriz .
İlk önce karşılaştıralım a[k/2]ve b[k/2]. Let b[k/2]> a[k/2]. Bu nedenle b[i]i> k / 2 olan her şeyi de atabiliriz .

Şimdi hepsine sahibiz a[i], burada i <k ve hepsi b[i], cevabı bulmak için i <k / 2.

Bir sonraki adım nedir?


6
Tüm bu adımlar atamaya dahil edildi mi yoksa yukarıdaki adımlar algoritmanızın başlangıcı mı?
Kendrick

18
Yukarıdaki adımlar benimdir.
Michael

Is O(logN + logM)sadece kth elemanını bulmak için gereken sürede atıfta? Sendikaya önceden ön işlem yapılabilir mi?
David Weiser

1
@David. Ön işleme beklenmez.
Michael

3
Dizilerde kopyalara izin verilir mi?
David Weiser

Yanıtlar:


48

Anladın, devam et! Ve indekslere dikkat edin ...

Biraz basitleştirmek için N ve M'nin> k olduğunu varsayacağım, dolayısıyla buradaki karmaşıklık O (log k), yani O (log N + log M).

Sözde kod:

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

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

Gösteri için döngü değişmez i + j = k kullanabilirsiniz, ancak tüm ödevlerinizi yapmayacağım :)


14
Bu gerçek bir kanıt değil, ancak algoritmanın arkasındaki fikir, i + j = k'yi sürdürmemiz ve böyle i ve j'yi bulmamız, böylece a [i-1] <b [j-1] <a [i] ( veya tam tersi). Şimdi, 'a'da b [j-1]' den küçük i öğeleri ve 'b'de b [j-1]' den küçük j-1 öğeleri olduğu için, b [j-1] i + j-1'dir + 1 = k'inci en küçük öğe. Böyle bir i, j'yi bulmak için, algoritma diziler üzerinde ikili bir arama yapar. Mantıklı?
Jules Olléon

8
O (log k) nasıl O (log n + log m) olur?
Rajendra Uppal

7
Dizi 1'deki tüm değerler dizi 2'deki değerlerden önce gelirse bu işe yaramaz.
John Kurlak

3
Neden ilk başta adım olarak k / 4 kullandınız?
Maggie

2
@JohnKurlak'ın belirttiği gibi, bütün a'nın b'den
Jeremy S.

69

Umarım bu sorunun sorulmasının üzerinden bir yıldan fazla zaman geçtiği için ödevinize cevap vermemişimdir. İşte log (len (a) + len (b)) süresini alacak bir kuyruk özyinelemeli çözüm.

Varsayım: Girişler doğrudur. yani k, [0, len (a) + len (b)] aralığındadır

Temel durumlar:

  • Dizilerden birinin uzunluğu 0 ise, yanıt ikinci dizinin k. Öğesidir.

Azaltma adımları:

  • Orta endeksi a+ orta dizini bşundan küçükse:k
    • Orta öğesi, öğesinin orta öğesinden abüyükse , ayarlamanın bilk yarısını göz ardı edebiliriz .bk
    • yoksa ilk yarısını dikkate almayın a, ayarlayın k.
  • Aksi takdirde k, orta endekslerin toplamından küçükse ave b:
    • Orta öğesi, öğesinin orta öğesinden abüyükse, öğenin bikinci yarısını güvenle göz ardı edebiliriz.a
    • aksi takdirde ikinci yarısını görmezden gelebiliriz b

Kod:

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

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

Lütfen benim çözümümün her çağrıda daha küçük dizilerin yeni kopyalarını oluşturmak olduğunu unutmayın; bu, yalnızca orijinal dizilerdeki başlangıç ​​ve bitiş indekslerini ileterek kolayca ortadan kaldırılabilir.


4
neden kthlargest()ona - (k+1)en küçük öğeleri döndürür diyorsunuz , örneğin, 1ikinci en küçük öğedir, 0,1,2,3yani işleviniz döner sorted(a+b)[k].
jfs

2
Kodunuzu C ++ 'ya çevirdim . İşe yarıyor gibi görünüyor
jfs

1
a ve b'nin orta indekslerinin toplamını k ile karşılaştırmanın neden önemli olduğunu açıklayabilir misiniz?
Maggie

3
İndirgeme adımlarında, çalışma zamanını logaritmik hale getirmek için dizilerin birindeki uzunluğuyla orantılı bir dizi öğeden kurtulmak önemlidir. (Burada yarısından kurtuluyoruz). Bunu yapmak için, yarısından birini güvenle göz ardı edebileceğimiz bir dizi seçmemiz gerekiyor. Bunu nasıl yaparız? Emin bir şekilde ortadan kaldırarak, bildiğimiz yarının k'inci öğeye sahip olmayacağından emin oluruz.
lambdapilgrim

1
K'yi dizilerin yarı uzunluklarının toplamıyla karşılaştırmak bize dizilerden birinin hangi yarısının elimine edilebileceği hakkında bilgi verir. Eğer k, yarı uzunlukların toplamından büyükse, dizilerden birinin ilk yarısının elenebileceğini biliyoruz. K daha küçükse tam tersi. Aynı anda her diziden bir yarısını eleyemeyeceğimizi unutmayın. Hangi dizinin hangi yarısının ortadan kaldırılacağına karar vermek için, her iki dizinin de sıralı olduğu gerçeğinden yararlanıyoruz, bu nedenle k, yarı uzunlukların toplamından büyükse, orta elemanı dizinin ilk yarısını ortadan kaldırabiliriz. iki orta eleman. Tersine.
lambdapilgrim

34

Birçok kişi bu "iki sıralı diziden k. En küçük eleman" sorusunu yanıtladı, ancak genellikle yalnızca genel fikirlerle, açık bir çalışma kodu veya sınır koşulları analizi ile değil.

Burada, bazı acemilerin doğru çalışan Java kodumla anlamalarına yardımcı olmak için yaptığım yolu dikkatlice detaylandırmak istiyorum. A1ve sırasıyla uzunluklu ve uzunluk olarak A2sıralanmış iki artan dizidir . Bu iki dizinin birleşiminden k'inci en küçük elemanı bulmamız gerekiyor. Burada makul olarak bunu varsayıyoruz , bu da bunu ima ediyor ve ikisi de boş olamaz.size1size2(k > 0 && k <= size1 + size2)A1A2

Öncelikle bu soruya yavaş bir O (k) algoritması ile yaklaşalım. Yöntem, her iki dizinin ilk öğe karşılaştırmaktır, A1[0]ve A2[0]. Küçük olanı alın, cebimize A1[0]koyun. Sonra karşılaştırma A1[1]ile A2[0], vb. Cebimiz kelemanlara ulaşana kadar bu işlemi tekrarlayın . Çok önemli: İlk adımda sadece cebimizde taahhüt verebiliriz A1[0]. Dahil edemez veya hariç tutamayız A2[0]!!!

Aşağıdaki O (k) kodu size doğru yanıttan önce bir öğe verir. Burada fikrimi ve analiz sınır koşulunu göstermek için kullanıyorum. Bundan sonra doğru kodum var:

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

En güçlü fikir, her döngüde her zaman temel durum yaklaşımını kullanmamızdır. Mevcut en küçük unsuru taahhüt ettikten sonra, hedefe bir adım daha yaklaşırız: k'inci en küçük element. Asla ortaya atlamayın ve kendinizi şaşırtmayın ve kaybolmayın!

Yukarıdaki kod temel durumunu gözlemleyerek k == 1, k == size1+size2ve bununla birleştirin A1ve A2ikisi de boş olamaz. Mantığı daha özlü bir stile dönüştürebiliriz.

İşte yavaş ama doğru çalışan bir kod:

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

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

Şimdi O (log k) 'de çalışan daha hızlı bir algoritma deneyebiliriz. Benzer şekilde, A1[k/2]şununla karşılaştırın A2[k/2]; eğer A1[k/2]daha küçük, o andan itibaren tüm unsurları A1[0]için A1[k/2]bizim cebinde olmalıdır. Buradaki fikir, her döngüde yalnızca bir öğeye bağlanmak değildir; ilk adım k/2öğeleri içerir . Yine, dahil edemez veya hariç tutamayızA2[0] için A2[k/2]neyse. Yani ilk adımda k/2elementlerden daha fazlasına gidemeyiz . İkinci adım için, k/4elementlerden ötesine gidemeyiz ...

Her adımdan sonra, k'inci öğeye çok yaklaşıyoruz. Aynı zamanda, ulaşana kadar her adım daha da küçülür.(step == 1) olan (k-1 == index1+index2). O zaman basit ve güçlü temel duruma tekrar başvurabiliriz.

İşte çalışan doğru kod:

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

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

Bazı insanlar endişelenebilir (index1+index2) k-1'in üzerinden atlarsaTemel durumu gözden kaçırabilir miyiz (k-1 == index1+index2)? Bu imkansız. 0,5 + 0,25 + 0,125 ekleyebilirsin ve asla 1'in ötesine geçemezsin.

Elbette yukarıdaki kodu yinelemeli algoritmaya dönüştürmek çok kolaydır:

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

Umarım yukarıdaki analiz ve Java kodu anlamanıza yardımcı olabilir. Ancak kodumu asla ödeviniz olarak kopyalamayın! Şerefe;)


1
Harika açıklamalarınız ve cevabınız için çok teşekkür ederim, +1 :)
Hengameh

İlk kodda, else if (A1[size1 - 1].compareTo(A2[size2 - 1]) < 0) yerine olmamalı else if (A1[size1 - 1].compareTo(A2[size2 - 1]) > 0)mı? (KthSmallestSlowWithFault kodunda)
Hengameh

Teşekkür ederim @ Fei. Harika açıklama. Bu sorunla ilgili olarak internette kaç tane yanlış cevap dolaşıyor olması şaşırtıcı. Bu soruyla ilgili olarak kendisine SO'da kabul edilen cevabın her zaman yanlış olması daha da şaşırtıcıdır. Görünüşe göre kimse cevapları test etmeyi umursamıyor.
Kaptan Fogetti

Belki bazı adımlardan sonra (15) O (k) çözümüne kesilir, çünkü adım aralığı oldukça hızlı azalır.
Gökyüzü

1
Özyinelemeli çağrıların hiçbirinde, A1 veya A2 boyutları küçültülmez.
Aditya Joshee

5

İşte @ lambdapilgrim çözümünün C ++ yinelemeli sürümü ( oradaki algoritmanın açıklamasına bakın):

#include <cassert>
#include <iterator>

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

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

Tüm 0 <= n < (size(a) + size(b))dizinler için çalışır ve O(log(size(a)) + log(size(b)))karmaşıktır.

Misal

#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

İlk k sayı, 2 sıralı dizide k'inci sayı ve n sıralı dizide denemem:

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

Hata ayıklama araçlarıyla birlikte kodun tamamı şu adreste bulunabilir: https://github.com/brainclone/teasers/tree/master/kth


3

Jules Olleon'un çözümüne dayanan kodum:

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
Bu bazı durumlarda işe yaramayacaktır. Örneğin, int a2 [] = {1,2,3,4, 5}; int a1 [] = {5,6,8,10,12}; getNth (a1, a2, 7). Dizinin dizini sınırın dışına çıkacaktır.
Jay

2

İşte benim C'deki uygulamam, algoritma için @Jules Olléon'un açıklamalarına başvurabilirsiniz: algoritmanın arkasındaki fikir, i + j = k'yi sürdürmemiz ve böyle i ve j'yi bulmamızdır, böylece a [i-1] <b [j-1] <a [i] (veya tam tersi). Şimdi 'a'da b [j-1]' den küçük i öğeleri ve 'b'de b [j-1]' den küçük j-1 öğeleri olduğu için, b [j-1] i + j-1'dir + 1 = k'inci en küçük öğe. Böyle bir i, j'yi bulmak için, algoritma diziler üzerinde ikiye bölünmüş bir arama yapar.

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

İşte benim çözümüm. C ++ kodu, k'inci en küçük değeri ve bir döngü kullanarak k'inci en küçük değeri elde etmek için yineleme sayısını yazdırır, ki bu benim görüşüme göre log (k) sırasındadır. Bununla birlikte, kod, k'nin bir sınırlama olan birinci dizinin uzunluğundan daha küçük olmasını gerektirir.

#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

Yukarıda verilen ilk sözde kod, birçok değer için çalışmaz. Örneğin, burada iki dizi var. int [] a = {1, 5, 6, 8, 9, 11, 15, 17, 19}; int [] b = {4, 7, 8, 13, 15, 18, 20, 24, 26};

İçinde k = 3 ve k = 9 için çalışmadı. Başka bir çözümüm var. Aşağıda verilmiştir.

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

}

Ama ... k = 5 için de çalışmıyor. Basit olmasına izin vermeyen bu çift / tek yakalama var.


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

İşte java'daki çözümüm. Daha da optimize etmeye çalışacak

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

    }
}

Bu, harika youtube videosundaki Algo'dan esinlenmiştir.


1

Kod karmaşıklığına bağlantı (log (n) + log (m))

Koda Bağlantı (log (n) * log (m))

Uygulama (log (n) + log (m)) çözeltisi

Soruna açıklamamı eklemek istiyorum. Bu, iki dizinin sıralı olduğu gerçeğini kullanmamız gereken klasik bir sorundur. bize sz1 boyutunda arr1 ve sz2 boyutunda arr2 sıralı diziler verildi

a) Varsayalım eğer

K'nin geçerli olup olmadığı kontrol ediliyor

k> (sz1 + sz2)

o zaman her iki sıralı dizinin birleşiminde k'inci en küçük elemanı bulamayız ryt Yani geçersiz veri döndür. b) Şimdi yukarıdaki koşul yanlışsa ve geçerli ve uygulanabilir k değerine sahipsek,

Uç Durumları Yönetme

K = 1,2 ve k = (sz1 + sz2-1), (sz1 + sz2) vb. Kenar durumlarını kapsamak için dizileri öndeki -sonsuz değerlerle ve sonunda + sonsuz değerlerle ekleyeceğiz.

Şimdi her iki dizinin de boyutu (sz1 + 2) ve (sz2 + 2) sırasıyla

Ana Algoritma

Şimdi arr1 üzerinde ikili arama yapacağız. Arr1 üzerinde ikili arama yapacağız i indeksini arayacağız, startIndex <= i <= endIndex

öyle ki, {(i + j) = k} kısıtını kullanarak arr2'de karşılık gelen j indisini bulursak, o zaman eğer

eğer (arr2 [j-1] <arr1 [i] <arr2 [j]) ise , arr1 [i] k'inci en küçüktür (Durum 1)

Aksi takdirde (arr1 [i-1] <arr2 [j] <arr1 [i]) , o zaman arr2 [i] k'inci en küçüktür (Durum 2)

else ya arr1 [i] <arr2 [j-1] <arr2 [j] (Durum3) anlamına gelir

veya arr2 [j-1] <arr2 [j] <arr1 [i] (Durum4)

Biliyoruz ki bu yana kth en küçük elemanı o daha (k-1) elemanları daha küçük sahiptir diziler ryt hem birlik içinde? Yani,

In Durum 1 , ne yaptığını, biz arr1 (k-1) daha küçük elemanların toplam vardır sağlamıştır [I] için arr1 [I] 'de arr1 dizi i-1 bildiğimiz daha sayısında (ARR2 [daha küçük elemanlar j-1] <arr1 [i] <arr2 [j]) ve arr2'deki arr1 [i] 'den küçük elemanların sayısı j-1'dir çünkü j (i-1) + (j-1) = (k -1) Yani k'inci en küçük eleman arr1 [i] olacaktır

Ancak cevap her zaman ilk diziden yani arr1'den gelmeyebilir, bu yüzden durum 1'e benzer şekilde karşılayan durum2'yi kontrol ettik çünkü (i-1) + (j-1) = (k-1). Şimdi eğer (arr1 [i-1] <arr2 [j] <arr1 [i]) elde edersek, her iki dizinin birleşiminde arr2 [j] 'den daha küçük toplam k-1 elemanımız olur, bu yüzden onun k'inci en küçük elemanıdır.

Gelen case3 , durumda, 1 veya durum 2'nin herhangi için oluşturmak üzere, biz i artırmak için ve j, sınırlama kullanarak uygun bulunacaktır gerekmez {(i + j) = k} yani sağa kısmına ikili arama hareket yani yapmak startIndex = middleIndex

Gelen case4 , durumda, 1 veya durum 2'nin herhangi için oluşturmak üzere, biz i azaltma ve j, kısıtlama {(i + j) = k} yani sol kısmına ikili arama hareket yani yapmak endIndex = middleIndex kullanılarak uygun bulunacaktır gerekmez .

Şimdi startIndex = 1 ve endIndex = ?? ile arr1 üzerinden ikili aramanın başlangıcında startIndex ve endIndex'e nasıl karar vereceğiz. Karar vermemiz gerekiyor.

K> sz1, endIndex = (sz1 + 1) ise, aksi takdirde endIndex = k;

Çünkü eğer k ilk dizinin boyutundan büyükse, arr1 dizisinin tamamı üzerinde ikili arama yapmak zorunda kalabiliriz, aksi takdirde onun sadece ilk k elemanını almamız gerekir çünkü sz1-k elemanları k'inci en küçüğünün hesaplanmasına asla katkıda bulunamaz.

Aşağıda Gösterilen KOD

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

}

Karmaşıklığın Çözümü için (log (n) * log (m))

Her bir i için j'nin {(i-1) + (j-1) = (k-1)} kısıtlaması kullanılarak bulunabileceği gerçeğinin avantajını kullanarak kaçırdım Yani her ii için ikinci diziye ikili aramayı daha da uyguluyordum arr2 [j] <= arr1 [i] olacak şekilde j'yi bulmak için. Böylece bu çözüm daha da optimize edilebilir


1

Temel olarak, bu yaklaşımla her adımda k / 2 öğeleri atabilirsiniz. K, k => k / 2 => k / 4 => ... 'den 1'e ulaşana kadar özyinelemeli olarak değişecektir. Yani, Zaman Karmaşıklığı O (logk)

K = 1'de, iki diziden en düşük olanını elde ederiz.

Aşağıdaki kod JAVA'da. Lütfen koddaki 1 (-1) 'i indislerden çıkardığımıza dikkat edin, çünkü Java dizisinin indeksi 0'dan başlar ve 1'den değil, örn. k = 3, bir dizinin 2. dizinindeki öğe tarafından temsil edilir.

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

Zaman Karmaşıklığı O (log (min (n, m)))


1

Burada bulduğum cevapların çoğu her iki diziye odaklanıyor. iyi olsa da, ilgilenmemiz gereken çok sayıda uç durum olduğu için uygulaması daha zordur. Bunun yanı sıra, uygulamaların çoğu özyinelemelidir, bu da özyineleme yığınının alan karmaşıklığını ekler. Bu yüzden, her iki diziye de odaklanmak yerine, sadece daha küçük diziye odaklanmaya ve sadece daha küçük dizi üzerinde ikili aramayı yapmaya ve ikinci dizinin işaretçisini ilk Dizideki işaretçinin değerine göre ayarlamaya karar verdim. Aşağıdaki uygulama O(log(min(n,m))ile O(1)uzay karmaşıklığının karmaşıklığına sahibiz .

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

[low, high]Dizi için bir aralığımız var ave algoritmada ilerledikçe bu aralığı daraltırız. sizeAnasıl öğelerin birçoğunu gösterileri kkalemleri diziden olan ave değerinden türemiştir lowve high. sizeBdeğeri öyle bir şekilde hesaplamamız dışında aynı tanımdır sizeA+sizeB=k. Bu iki sınırdaki değerlere dayanarak, dizide sağ tarafa uzanmamız aveya sol tarafa doğru daralmamız gerektiği sonucuna varılır. Biz aynı pozisyonda sıkışmış eğer biz çözüm bulduk ve biz konumunda değerlerinin max dönecektir anlamına gelir sizeA-1dan ave sizeB-1gelen b.


0

Bu kodu kontrol edin.

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

Açıklama sağlayın
Abhijit Sarkar

0

İki Sıralanmış Dizi Birliğinde k. En Küçük Öğeyi Bulmak için C # kodunun altında. Zaman Karmaşıklığı: 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);

            }
        }

hata yok, kodumu
SO'ya

1
Teşekkürler sammy333, kodu güncelledim. şimdi çalışıyor
Piyush Patel

(Hesaplamak etmeyin midAdan endAeğer k < nkısa diziler için kontrol, ile başlayan. return B[startB + k - 1];.)
ihtiyar adam
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.