Як знайти найменший елемент k у об'єднанні двох відсортованих масивів?


106

Це питання домашнього завдання. Кажуть, це займає O(logN + logM)місце Nта Mдовжину масивів.

Назвемо масиви aта b. Очевидно, що ми можемо ігнорувати все a[i]і b[i]де i> k.
Спочатку порівняємо a[k/2]і b[k/2]. Нехай b[k/2]> a[k/2]. Тому ми можемо відкинути також усі b[i], де i> k / 2.

Тепер у нас є всі a[i], де i <k і всі b[i], де i <k / 2, щоб знайти відповідь.

Який наступний крок?


6
Чи були всі ці етапи включені у завдання, чи вказані вище кроки є початком вашого алгоритму?
Кендрік

18
Наведені вище дії - мої.
Майкл

Є O(logN + logM)тільки зі посиланням на той час, який потрібен , щоб знайти елемент його? Чи можна заздалегідь здійснити попередню обробку для об'єднання?
Девід Вейзер

1
@David. Попередня обробка не очікується.
Майкл

3
Чи дозволені дублікати в масивах?
Девід Вейзер

Відповіді:


48

Ви отримали це, просто продовжуйте! І будьте обережні з показниками ...

Щоб трохи спростити, я вважаю, що N і M є> k, тому складність тут O (log k), яка O (log N + log M).

Псевдокод:

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]

Для демонстрації ви можете використовувати цикл інваріант i + j = k, але я не буду виконувати всі ваші домашні завдання :)


14
Це не є реальним доказом, але ідея алгоритму полягає в тому, що ми підтримуємо i + j = k і знаходимо такі i і j, щоб a [i-1] <b [j-1] <a [i] ( або навпаки). Тепер оскільки елементи i в елементах 'a' менше, ніж b [j-1], а j-1 елементів у 'b' менше, ніж b [j-1], b [j-1] є i + j-1 + 1 = найменший елемент kth. Щоб знайти такі i, j, алгоритм здійснює дихотомічний пошук на масивах. Має сенс?
Jules Olléon

8
Звідки O (log k) - O (log n + log m)?
Rajendra Uppal

7
Це не працює, якщо всі значення в масиві 1 переходять до значень масиву 2.
Джон Курлак,

3
Чому спочатку ви використовували k / 4 як крок?
Меггі

2
Як згадував @JohnKurlak, він не працює для значень, де ціле a менше b, див. Repl.it/HMYf/0
Джеремі С.

69

Я сподіваюсь, що я не відповідаю на ваше домашнє завдання, оскільки минуло більше року з моменту цього питання. Ось хвостовий рекурсивний розчин, який потребуватиме журналу (len (a) + len (b)) час.

Припущення: вводи правильні. тобто k знаходиться в межах [0, len (a) + len (b)]

Базові корпуси:

  • Якщо довжина одного з масивів дорівнює 0, відповідь - kth елемент другого масиву.

Етапи скорочення:

  • Якщо середній індекс a+ середній індекс bстановить меншеk
    • Якщо середній елемент елемента aбільше середнього елемента b, ми можемо проігнорувати першу половину b, скорегувати k.
    • інше ігноруйте першу половину a, коригуйте k.
  • Інше, якщо kменша сума середніх індексів aі b:
    • Якщо серединний елемент aбільше середнього елемента b, ми можемо сміливо ігнорувати другу половинуa
    • інакше ми можемо ігнорувати другу половину b

Код:

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)

Зауважте, що моє рішення створює нові копії менших масивів у кожному виклику, це можна легко усунути, лише пройшовши індекси початку та кінця на початкових масивах.


4
чому ви називаєте це kthlargest()поверненням - (k+1)найменшими елементами, наприклад, 1другим найменшим елементом, 0,1,2,3тобто ваша функція повертається sorted(a+b)[k].
jfs

2
Я перетворив ваш код на C ++ . Здається, працює
jfs

1
чи можете ви поясніть, чому важливо порівняти суму середніх індексів a і b з k?
Меггі

3
На етапах скорочення важливо позбутися ряду елементів в одному з масивів, пропорційних його довжині, щоб зробити логарифмічний час виконання. (Ось ми позбулися половини). Для цього нам потрібно вибрати один масив, одну з половинок якого ми можемо сміливо ігнорувати. Як ми це робимо? Впевнено усуваючи половину, ми точно знаємо, що kth елемент не матиме.
lambdapilgrim

1
Порівнюючи k із сумою півдовжин масивів дає нам інформацію про те, яку половину одного з масивів можна усунути. Якщо k більша від суми півдовжин, ми знаємо, що перша половина одного з масивів може бути усунена. Навпроти, якщо k менший. Зауважте, що ми не можемо усунути одну половину з кожного масиву відразу. Вирішуючи, яку половину масиву вилучити, ми використовуємо той факт, що обидва масиви відсортовані, тож якщо k більша від суми половини, ми можемо усунути першу половину масиву, середній елемент якого менший від два середніх елемента. Навпаки.
lambdapilgrim

34

Багато людей відповіли на це "kth найменший елемент з двох відсортованих масивів", але, як правило, лише із загальними ідеями, а не чітким робочим кодом або аналізом граничних умов.

Тут я хотів би ретельно розібратися з тим, як я пішов, хоча допомогти новачкові зрозуміти, з моїм правильним робочим кодом Java. A1і A2є двома відсортованими висхідними масивами, відповідно size1і з size2довжиною. Нам потрібно знайти k-й найменший елемент з об'єднання цих двох масивів. Тут ми розумно припустити , що (k > 0 && k <= size1 + size2), звідки слід , що A1і A2не можуть бути порожніми.

Спочатку давайте підійдемо до цього питання за допомогою повільного алгоритму O (k). Метод полягає в порівнянні першого елемента і масиву, A1[0]і A2[0]. Візьміть менший, скажіть A1[0]у нашу кишеню. Потім порівнюйте A1[1]з A2[0]тощо. Повторіть цю дію, поки наша кишеня не досягне kелементів. Дуже важливо: на першому кроці ми можемо взяти на себе зобов’язання лише A1[0]у своїй кишені. НЕ МОЖЕ НЕ включати або виключати A2[0]!!!

Наступний код O (k) дає вам один елемент перед правильною відповіддю. Тут я використовую його для показу своєї ідеї та аналізу межового стану. У мене є правильний код після цього:

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

Найпотужніша ідея полягає в тому, що в кожному циклі ми завжди використовуємо підхід базового випадку. Після прихильності до поточного найменшого елемента ми наближаємось на крок ближче до цілі: k-го найменшого елемента. Ніколи не стрибайте в середину і не робіть себе розгубленим і загубленим!

Дотримуючись вищезазначеного базового випадку коду k == 1, k == size1+size2, комбінуйте його A1і A2не може бути обом порожнім. Ми можемо перетворити логіку нижче на більш стислий стиль.

Ось повільний, але правильний робочий код:

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

Тепер ми можемо спробувати більш швидкий алгоритм, що працює в O (log k). Аналогічно порівняти A1[k/2]з A2[k/2]; якщо A1[k/2]менше, то всі елементи від A1[0]до A1[k/2]повинні бути в нашій кишені. Ідея полягає в тому, щоб не просто привласнювати один елемент у кожному циклі; перший крок містить k/2елементи. Знову ж таки, ми НЕ МОЖЕ будь-коли включати або виключати A2[0]їх A2[k/2]. Отже, на першому кроці ми не можемо досягти більше k/2елементів. Для другого кроку ми не можемо досягти більше k/4елементів ...

Після кожного кроку ми наближаємося до k-го елемента. При цьому кожен крок стає все меншим і меншим, поки ми не досягнемо (step == 1), що є (k-1 == index1+index2). Тоді ми можемо знову звернутися до простого та потужного базового випадку.

Ось робочий правильний код:

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

Деякі люди можуть переживати, що робити, якщо (index1+index2)перестрибнути через k-1? Чи можемо ми пропустити базовий випадок (k-1 == index1+index2)? Це неможливо. Ви можете додати 0,5 + 0,25 + 0,125 ..., і ви ніколи не вийдете за межі 1.

Звичайно, перетворити вищезазначений код в рекурсивний алгоритм дуже просто:

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

Сподіваюсь, що наведений вище аналіз та код Java можуть допомогти вам зрозуміти. Але ніколи не копіюйте мій код як домашнє завдання! Ура;)


1
Дякую вам за ваші чудові пояснення та відповідь, +1 :)
Hengameh

У першому коді не повинно бути else if (A1[size1 - 1].compareTo(A2[size2 - 1]) < 0) замість else if (A1[size1 - 1].compareTo(A2[size2 - 1]) > 0)? (In kthSmallestSlowWithFault code)
Hengameh

Дякую @Fei Чудове пояснення. Дивно, скільки неправильних відповідей циркулює в Інтернеті щодо цієї проблеми. Ще більше дивує, що прийнята відповідь їй на ТА щодо цього питання завжди є неправильною. Здається, нікому не цікаво перевірити відповіді.
Капітан Фогетті

Можливо, вирізання рішення O (k) після деяких кроків (сказано 15), оскільки діапазон кроків зменшується досить швидко.
Sky

1
У жодному з рекурсивних викликів розміри А1 або А2 не зменшуються.
Aditya Joshee

5

Ось C ++ ітеративна версія рішення @ lambdapilgrim (див. Пояснення алгоритму там):

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

Він працює для всіх 0 <= n < (size(a) + size(b))індексів і має O(log(size(a)) + log(size(b)))складність.

Приклад

#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

Моя спроба для перших k чисел, kth числа у 2 відсортованих масивах та у n відсортованих масивах:

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

Повний код з утилітами налагодження можна знайти за посиланням: https://github.com/brainclone/teasers/tree/master/kth


3

Ось мій код, заснований на рішенні Жуля Оллеона:

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
У деяких випадках це не спрацює. Наприклад, int a2 [] = {1,2,3,4, 5}; int a1 [] = {5,6,8,10,12}; getNth (a1, a2, 7). Індекс масиву вийде за межі.
Джей

2

Ось моя реалізація в C, ви можете звернутися до пояснення алгоритму @Jules Olléon: ідея алгоритму полягає в тому, щоб ми підтримували i + j = k, і знаходимо такі i і j, щоб a [i-1] <b [j-1] <a [i] (або навпаки). Тепер оскільки елементи i в елементах 'a' менше, ніж b [j-1], а j-1 елементів у 'b' менше b [j-1], b [j-1] є i + j-1 + 1 = найменший елемент kth. Щоб знайти такі i, j, алгоритм здійснює дихотомічний пошук на масивах.

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

Ось моє рішення. Код C ++ друкує найменше значення kth, а також кількість ітерацій, щоб отримати kth найменше значення за допомогою циклу, який, на мою думку, знаходиться в порядку log (k). Код, однак, вимагає, щоб k був меншим, ніж довжина першого масиву, що є обмеженням.

#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

Перший псевдо-код, наданий вище, не працює для багатьох значень. Наприклад, ось два масиви. int [] a = {1, 5, 6, 8, 9, 11, 15, 17, 19}; int [] b = {4, 7, 8, 13, 15, 18, 20, 24, 26};

Це не спрацювало при k = 3 і k = 9 в ньому. У мене є інше рішення. Це наведено нижче.

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

}

Але ... це також не працює для k = 5. Існує цей парний / непарний вилов k, який не дозволяє йому бути простим.


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

Ось моє рішення в java. Спробуємо додатково її оптимізувати

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

    }
}

Це натхненно Algo на чудовому відео YouTube


1

Посилання на складність коду (log (n) + log (m))

Посилання на код (log (n) * log (m))

Впровадження рішення (log (n) + log (m))

Я хотів би додати своє пояснення до проблеми. Це класична проблема, коли ми маємо використати той факт, що два масиви відсортовані. нам дали два відсортовані масиви arr1 розміром sz1 та arr2 розміром sz2

а) Допустимо, якщо

Перевірка Якщо k є дійсним

k є> (sz1 + sz2)

тоді ми не можемо знайти kth найменший елемент в об'єднанні обох відсортованих масивів ryt. Тому повертаємо невірні дані. b) Тепер, якщо вищезазначена умова не відповідає дійсності, і ми маємо дійсне і здійснене значення k,

Управління крайовими справами

Додамо обидва масиви за значеннями -infinity на фронті та + знаками нескінченності в кінці, щоб охопити крайні випадки k = 1,2 та k = (sz1 + sz2-1), (sz1 + sz2) тощо.

Тепер обидва масиви мають розміри (sz1 + 2) та (sz2 + 2) відповідно

Основний алгоритм

Тепер ми зробимо двійковий пошук на arr1. Ми зробимо двійковий пошук на arr1, шукаючи індекс i, startIndex <= i <= endIndex

такий, що якщо ми знайдемо відповідний індекс j в arr2, використовуючи обмеження {(i + j) = k}, тоді, якщо

якщо (arr2 [j-1] <arr1 [i] <arr2 [j]) , то arr1 [i] - найменший kth (випадок 1)

інакше, якщо (arr1 [i-1] <arr2 [j] <arr1 [i]) , тоді arr2 [i] - найменший kth (випадок 2)

інше означає або arr1 [i] <arr2 [j-1] <arr2 [j] (Case3)

або arr2 [j-1] <arr2 [j] <arr1 [i] (Case4)

Оскільки ми знаємо, що найменший елемент k має (k-1) елементів менших за нього в об'єднанні обох масивів ryt? Так,

У випадку1 , що ми зробили, ми переконалися, що в arr1 [i] є загалом (k-1) менших елементів, оскільки в масиві arr1 елементи, менші за arr1 [i], є i-1 за кількістю, ніж ми знаємо (arr2 [ j-1] <arr1 [i] <arr2 [j]), а кількість елементів, менших за arr1 [i] в ​​arr2, є j-1, оскільки j знайдено за допомогою (i-1) + (j-1) = (k -1) Отже, найменший елемент kth буде arr1 [i]

Але відповідь не завжди може надходити з першого масиву, тобто arr1, тому ми перевірили на випадок case2, який також задовольняє аналогічно випадку 1, тому що (i-1) + (j-1) = (k-1). Тепер, якщо у нас є (arr1 [i-1] <arr2 [j] <arr1 [i]), у нас в об'єднанні обох масивів є k-1 елементів, менших, ніж arr2 [j], тому його kth найменший елемент.

У випадку3 , щоб сформувати його у будь-якому випадку 1 або випадку 2, нам потрібно збільшити i, j буде знайдено відповідно до обмеження {(i + j) = k}, тобто в двійковому пошуку перейти до правої частини, тобто зробити startIndex = middleIndex

У випадку4 , щоб сформувати його у будь-якому випадку 1 або випадку 2, нам потрібно декремент i і j буде знайдено відповідно до обмеження {(i + j) = k}, тобто в двійковому пошуку перейти до лівої частини, тобто зробити endIndex = middleIndex .

Тепер, як вирішити startIndex та endIndex на початку бінарного пошуку через arr1 з startindex = 1 та endIndex = ??. Нам потрібно вирішити.

Якщо k> sz1, endIndex = (sz1 + 1), інакше endIndex = k;

Оскільки, якщо k більший за розмір першого масиву, можливо, нам доведеться виконати двійковий пошук по всьому масиву arr1, інакше нам потрібно взяти лише перші k його елементи, оскільки елементи sz1-k ніколи не можуть сприяти обчисленню найменшого kth.

КОД показаний нижче

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

}

Для рішення складності (log (n) * log (m))

Щойно я пропустив, скориставшись тим, що для кожного i j можна знайти за допомогою обмеження {(i-1) + (j-1) = (k-1)} Отже, для кожного ii додатково застосовувався двійковий пошук на другому масиві знайти j таке, що arr2 [j] <= arr1 [i]. Отже, це рішення можна оптимізувати далі


1

В основному, завдяки такому підходу ви можете відкинути k / 2 елементи на кожному кроці. K рекурсивно змінюватиметься від k => k / 2 => k / 4 => ..., поки не досягне 1. Отже, складність часу дорівнює O (logk)

При k = 1 отримуємо найнижчий з двох масивів.

Наступний код є в JAVA. Зверніть увагу, що ми віднімаємо в коді 1 (-1) від індексів, тому що індекс масиву Java починається від 0, а не 1, наприклад. k = 3 представлений елементом у 2-му індексі масиву.

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

Часова складність: O (log (min (n, m)))


1

Більшість відповідей, які я знайшов тут, зосереджені на обох масивах. Хоча це добре, але це важче здійснити, оскільки існує маса крайніх справ, про які нам потрібно подбати. Крім того, більшість реалізацій є рекурсивними, що додає просторової складності стеку рекурсії. Тож замість того, щоб зосередитись на обох масивах, я вирішив просто зосередитись на меншому масиві та здійснити двійковий пошук лише на меншому масиві та відкоригувати вказівник для другого масиву на основі значення вказівника у першому масиві. Наступною реалізацією ми маємо складність O(log(min(n,m))з O(1)просторовою складністю.

    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]для масиву, aі ми звужуємо цей діапазон, рухаючись далі за алгоритмом. sizeAпоказує, скільки елементів з kелементів є з масиву, aі це походить від значення lowі high. sizeBце те саме визначення, за винятком того, що ми обчислюємо значення таким чином, що sizeA+sizeB=k. Виходячи зі значень на цих двох кордонах з висновком, що ми маємо поширюватися на правий бік у масиві aабо зменшуватись до лівої. якщо ми застрягли в тому самому положенні, це означає, що ми знайшли рішення і повернемо максимум значень у позиції sizeA-1з aі sizeB-1з b.


0

Перевірте цей код.

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

Надайте пояснення
Абхіджіт Саркар

0

Нижче C # код, щоб знайти k-й найменший елемент у об'єднанні двох відсортованих масивів. Час складності: 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);

            }
        }

немає помилок, я перевірив свій код до того, як я розмістив повідомлення в SO
Piyush Patel

1
Спасибі sammy333, я оновив код. тепер його робочий
Piyush Patel

(Не обчислити midAз endAякщо k < nПеревірити коротких масивів, починаючи з. return B[startB + k - 1];.)
глиняний глечик
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.