Como encontrar o k-ésimo menor elemento na união de duas matrizes classificadas?

106

Esta é uma questão de lição de casa. Eles dizem que leva O(logN + logM)onde Ne Msão os comprimentos das matrizes.

Vamos nomear os arrays ae b. Obviamente, podemos ignorar todos a[i]e b[i]onde i> k.
Primeiro, vamos comparar a[k/2]e b[k/2]. Deixe b[k/2]> a[k/2]. Portanto, podemos descartar também todos b[i], onde i> k / 2.

Agora temos all a[i], onde i <k e all b[i], onde i <k / 2 para encontrar a resposta.

Qual é o próximo passo?

Michael
fonte
6
Todas essas etapas foram incluídas na atribuição ou as etapas acima são o início do seu algoritmo?
Kendrick
18
As etapas acima são minhas.
Michael
Está se O(logN + logM)referindo apenas ao tempo que leva para encontrar o k-ésimo elemento? O pré-processamento pode ser feito para o sindicato com antecedência?
David Weiser
1
@David. Nenhum pré-processamento é esperado.
Michael
3
As duplicatas são permitidas nas matrizes?
David Weiser

Respostas:

48

Você tem isso, apenas continue! E cuidado com os índices ...

Para simplificar um pouco, assumirei que N e M são> k, então a complexidade aqui é O (log k), que é O (log N + log M).

Pseudo-código:

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]

Para a demonstração, você pode usar o invariante de loop i + j = k, mas não farei todo o seu dever de casa :)

Jules Olléon
fonte
14
Esta não é uma prova real, mas a ideia por trás do algoritmo é que mantemos i + j = k, e encontramos i e j de modo que a [i-1] <b [j-1] <a [i] ( ou do outro modo). Agora, uma vez que existem i elementos em 'a' menores que b [j-1] e elementos j-1 em 'b' menores que b [j-1], b [j-1] é i + j-1 + 1 = k ésimo menor elemento. Para encontrar tal i, j, o algoritmo faz uma pesquisa dicotômica nos arrays. Faz sentido?
Jules Olléon
8
Por que O (log k) é O (log n + log m)?
Rajendra Uppal
7
Isso não funciona se todos os valores na matriz 1 vierem antes dos valores na matriz 2.
John Kurlak
3
Por que você usou k / 4 como uma etapa no início?
Maggie,
2
Como @JohnKurlak mencionou, ele não funciona para valores onde o inteiro a é menor do que b veja repl.it/HMYf/0
Jeremy S.
69

Espero não estar respondendo ao seu dever de casa, pois já se passou mais de um ano desde que essa pergunta foi feita. Aqui está uma solução recursiva final que levará tempo log (len (a) + len (b)).

Premissa: as entradas estão certas. ou seja, k está no intervalo [0, len (a) + len (b)]

Casos básicos:

  • Se o comprimento de uma das matrizes for 0, a resposta será o k-ésimo elemento da segunda matriz.

Etapas de redução:

  • Se o índice médio de a+ índice médio de bfor menor quek
    • Se o elemento médio de afor maior do que o elemento médio de b, podemos ignorar a primeira metade de b, ajustar k.
    • senão ignore a primeira metade de a, ajuste k.
  • Caso contrário, se kfor menor que a soma dos índices médios de ae b:
    • Se o elemento médio de afor maior que o elemento médio de b, podemos ignorar com segurança a segunda metade dea
    • senão podemos ignorar a segunda metade de b

Código:

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)

Observe que minha solução é criar novas cópias de arrays menores em cada chamada; isso pode ser facilmente eliminado apenas passando os índices de início e fim nos arrays originais.

lambdapilgrim
fonte
4
por que você o chama de kthlargest()retorna (k+1)-ésimos menores elementos, por exemplo, 1é o segundo menor elemento, 0,1,2,3ou seja, seus retornos de função sorted(a+b)[k].
jfs
2
Converti seu código para C ++ . Parece funcionar
jfs
1
você poderia explicar por que é importante comparar a soma dos índices médios de aeb com k?
Maggie
3
Nas etapas de redução, é importante se livrar de um número de elementos em uma das matrizes proporcionais ao seu comprimento, a fim de tornar o tempo de execução logarítmico. (Aqui estamos nos livrando da metade). Para fazer isso, precisamos selecionar um array cuja uma das metades podemos ignorar com segurança. Como fazemos isso? Ao eliminar com segurança a metade, sabemos com certeza que não terá o k-ésimo elemento.
lambdapilgrim
1
Comparar k com a soma das metades dos arrays nos dá informações sobre qual metade de um dos arrays pode ser eliminada. Se k for maior do que a soma das metades, sabemos que a primeira metade de uma das matrizes pode ser eliminada. Oposto se k for menor. Observe que não podemos eliminar metade de cada array de uma vez. Para decidir qual metade de qual matriz eliminar, aproveitamos o fato de que ambas as matrizes são classificadas, portanto, se k for maior do que a soma das metades, podemos eliminar a primeira metade da matriz cujo elemento do meio é o menor dos dois elementos intermediários. Vice-versa.
lambdapilgrim
34

Muitas pessoas responderam a esta pergunta "k ésimo menor elemento de duas matrizes classificadas", mas geralmente com apenas idéias gerais, não um código de trabalho claro ou análise de condições de contorno.

Aqui eu gostaria de elaborá-lo cuidadosamente com a maneira que fiz para ajudar alguns novatos a entender, com meu código Java de trabalho correto. A1e A2são duas matrizes ascendentes classificadas, com size1e size2como comprimento, respectivamente. Precisamos encontrar o k-ésimo menor elemento da união dessas duas matrizes. Aqui, supomos razoavelmente que (k > 0 && k <= size1 + size2), o que implica isso A1e A2não pode ser vazio.

Primeiro, vamos abordar essa questão com um algoritmo lento O (k). O método consiste em comparar o primeiro elemento de ambas as matrizes A1[0]e A2[0]. Leve o menor, digamos A1[0], para o nosso bolso. Em seguida, compare A1[1]com A2[0]e assim por diante. Repita esta ação até que nosso bolso alcance os kelementos. Muito importante: na primeira etapa, só podemos nos comprometer A1[0]no nosso bolso. NÃO podemos incluir ou excluir A2[0]!!!

O código O (k) a seguir fornece um elemento antes da resposta correta. Aqui eu o uso para mostrar minha ideia e condição de contorno de análise. Eu tenho o código correto após este:

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

A ideia mais poderosa é que, em cada loop, sempre usamos a abordagem do caso base. Depois de comprometer o menor elemento atual, chegamos um passo mais perto do destino: o k-ésimo menor elemento. Nunca pule para o meio e fique confuso e perdido!

Observando o caso base do código acima k == 1, k == size1+size2, e combine com ele A1eA2 ambos não podem estar vazios. Podemos transformar a lógica abaixo em um estilo mais conciso.

Aqui está um código de trabalho lento, mas correto:

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

Agora podemos tentar um algoritmo mais rápido executado em O (log k). Da mesma forma, compare A1[k/2]com A2[k/2]; se A1[k/2]for menor, todos os elementos de A1[0]a A1[k/2]devem estar em nosso bolso. A ideia é não se comprometer apenas com um elemento em cada loop; a primeira etapa contém k/2elementos. Novamente, NÃO podemos incluir ou excluir A2[0]de A2[k/2]qualquer maneira. Portanto, na primeira etapa, não podemos ir além dos k/2elementos. Para a segunda etapa, não podemos ir além de k/4elementos ...

Após cada etapa, chegamos muito mais perto do k-ésimo elemento. Ao mesmo tempo, cada etapa fica cada vez menor, até chegarmos (step == 1), que é (k-1 == index1+index2). Então, podemos nos referir novamente ao caso básico simples e poderoso.

Aqui está o código de funcionamento correto:

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

Algumas pessoas podem se preocupar se (index1+index2)pular k-1? Podemos perder o caso básico(k-1 == index1+index2) ? Isso é impossível. Você pode somar 0,5 + 0,25 + 0,125 ... e nunca irá além de 1.

Claro, é muito fácil transformar o código acima em algoritmo recursivo:

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

Espero que a análise acima e o código Java possam ajudá-lo a entender. Mas nunca copie meu código como seu dever de casa! Felicidades ;)

Fei
fonte
1
Muito obrigado por suas ótimas explicações e resposta, +1 :)
Hengameh 01 de
No primeiro código, não deveria ser em else if (A1[size1 - 1].compareTo(A2[size2 - 1]) < 0) vez de else if (A1[size1 - 1].compareTo(A2[size2 - 1]) > 0)? (No código kthSmallestSlowWithFault)
Hengameh
Obrigado @Fei. Ótima explicação. É impressionante quantas respostas erradas estão circulando na internet a respeito desse problema. É ainda mais surpreendente que a resposta aceita no SO em relação a essa pergunta seja sempre a errada. Parece que ninguém se importa em testar as respostas.
Capitão Fogetti
Talvez corte para a solução O (k) após alguns passos (disse 15), pois o intervalo dos passos diminui muito rápido.
Sky
1
Em nenhuma das chamadas recursivas, os tamanhos de A1 ou A2 são reduzidos.
Aditya Joshee
5

Aqui está uma versão iterativa C ++ da solução de @ lambdapilgrim (veja a explicação do algoritmo lá):

#include <cassert>
#include <iterator>

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

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

Funciona para todos os 0 <= n < (size(a) + size(b))índices e tem O(log(size(a)) + log(size(b)))complexidade.

Exemplo

#include <functional> // greater<>
#include <iostream>

#define SIZE(a) (sizeof(a) / sizeof(*a))

int main() {
  int a[] = {5,4,3};
  int b[] = {2,1,0};
  int k = 1; // find minimum value, the 1st smallest value in a,b

  int i = k - 1; // convert to zero-based indexing
  int v = nsmallest_iter(a, a + SIZE(a), b, b + SIZE(b),
                         SIZE(a)+SIZE(b)-1-i, std::greater<int>());
  std::cout << v << std::endl; // -> 0
  return v;
}
jfs
fonte
4

Minha tentativa para os primeiros k números, k-ésimo número em 2 matrizes classificadas e em n matrizes classificadas:

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

O código completo com utilitários de depuração pode ser encontrado em: https://github.com/brainclone/teasers/tree/master/kth

Qichao Dong
fonte
3

Este é meu código baseado na solução de Jules Olleon:

int getNth(vector<int>& v1, vector<int>& v2, int n)
{
    int step = n / 4;

    int i1 = n / 2;
    int i2 = n - i1;

    while(!(v2[i2] >= v1[i1 - 1] && v1[i1] > v2[i2 - 1]))
    {                   
        if (v1[i1 - 1] >= v2[i2 - 1])
        {
            i1 -= step;
            i2 += step;
        }
        else
        {
            i1 += step;
            i2 -= step;
        }

        step /= 2;
        if (!step) step = 1;
    }

    if (v1[i1 - 1] >= v2[i2 - 1])
        return v1[i1 - 1];
    else
        return v2[i2 - 1];
}

int main()  
{  
    int a1[] = {1,2,3,4,5,6,7,8,9};
    int a2[] = {4,6,8,10,12};

    //int a1[] = {1,2,3,4,5,6,7,8,9};
    //int a2[] = {4,6,8,10,12};

    //int a1[] = {1,7,9,10,30};
    //int a2[] = {3,5,8,11};
    vector<int> v1(a1, a1+9);
    vector<int> v2(a2, a2+5);


    cout << getNth(v1, v2, 5);
    return 0;  
}  
soberbo
fonte
1
Isso não funcionará em alguns casos. Por exemplo, int a2 [] = {1,2,3,4, 5}; int a1 [] = {5,6,8,10,12}; getNth (a1, a2, 7). O índice da matriz sairá do limite.
Jay,
2

Aqui está minha implementação em C, você pode consultar a explicação de @Jules Olléon para o algoritmo: a ideia por trás do algoritmo é que mantemos i + j = k, e encontramos tais i e j de modo que a [i-1] <b [j-1] <a [i] (ou vice-versa). Agora, uma vez que existem i elementos em 'a' menores que b [j-1] e elementos j-1 em 'b' menores que b [j-1], b [j-1] é i + j-1 + 1 = k ésimo menor elemento. Para encontrar tal i, j, o algoritmo faz uma pesquisa dicotômica nas matrizes.

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);
   }
 }
não é ruim
fonte
2

Aqui está minha solução. O código C ++ imprime o menor valor k, bem como o número de iterações para obter o menor valor k usando um loop, que em minha opinião está na ordem de log (k). O código, entretanto, requer que k seja menor do que o comprimento da primeira matriz, o que é uma limitação.

#include <iostream>
#include <vector>
#include<math.h>
using namespace std;

template<typename comparable>
comparable kthSmallest(vector<comparable> & a, vector<comparable> & b, int k){

int idx1; // Index in the first array a
int idx2; // Index in the second array b
comparable maxVal, minValPlus;
float iter = k;
int numIterations = 0;

if(k > a.size()){ // Checks if k is larger than the size of first array
    cout << " k is larger than the first array" << endl;
    return -1;
}
else{ // If all conditions are satisfied, initialize the indexes
    idx1 = k - 1;
    idx2 = -1;
}

for ( ; ; ){
    numIterations ++;
    if(idx2 == -1 || b[idx2] <= a[idx1] ){
        maxVal = a[idx1];
        minValPlus = b[idx2 + 1];
        idx1 = idx1 - ceil(iter/2); // Binary search
        idx2 = k - idx1 - 2; // Ensures sum of indices  = k - 2
    }
    else{
        maxVal = b[idx2];
        minValPlus = a[idx1 + 1];
        idx2 = idx2 - ceil(iter/2); // Binary search
        idx1 = k - idx2 - 2; // Ensures sum of indices  = k - 2
    }
    if(minValPlus >= maxVal){ // Check if kth smallest value has been found
        cout << "The number of iterations to find the " << k << "(th) smallest value is    " << numIterations << endl;
        return maxVal;

    }
    else
        iter/=2; // Reduce search space of binary search
   }
}

int main(){
//Test Cases
    vector<int> a = {2, 4, 9, 15, 22, 34, 45, 55, 62, 67, 78, 85};
    vector<int> b = {1, 3, 6, 8, 11, 13, 15, 20, 56, 67, 89};
    // Input k < a.size()
    int kthSmallestVal;
    for (int k = 1; k <= a.size() ; k++){
        kthSmallestVal = kthSmallest<int>( a ,b ,k );
        cout << k <<" (th) smallest Value is " << kthSmallestVal << endl << endl << endl;
    }
}
Karthikeyan Sv
fonte
1

O primeiro pseudo código fornecido acima não funciona para muitos valores. Por exemplo, aqui estão duas matrizes. int [] a = {1, 5, 6, 8, 9, 11, 15, 17, 19}; int [] b = {4, 7, 8, 13, 15, 18, 20, 24, 26};

Não funcionou para k = 3 ek = 9 nele. Eu tenho outra solução. É apresentado a seguir.

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

}

Mas ... também não está funcionando para k = 5. Existe essa captura par / ímpar de k que não permite que seja simples.

sn.anurag
fonte
1
public class KthSmallestInSortedArray {

    public static void main(String[] args) {
        int a1[] = {2, 3, 10, 11, 43, 56},
                a2[] = {120, 13, 14, 24, 34, 36},
                k = 4;

        System.out.println(findKthElement(a1, a2, k));

    }

    private static int findKthElement(int a1[], int a2[], int k) {

        /** Checking k must less than sum of length of both array **/
        if (a1.length + a2.length < k) {
            throw new IllegalArgumentException();
        }

        /** K must be greater than zero **/
        if (k <= 0) {
            throw new IllegalArgumentException();
        }

        /**
         * Finding begin, l and end such that
         * begin <= l < end
         * a1[0].....a1[l-1] and
         * a2[0]....a2[k-l-1] are the smallest k numbers
         */
        int begin = Math.max(0, k - a2.length);
        int end = Math.min(a1.length, k);

        while (begin < end) {
            int l = begin + (end - begin) / 2;

            /** Can we include a1[l] in the k smallest numbers */
            if ((l < a1.length) &&
                    (k - l > 0) &&
                    (a1[l] < a2[k - l - 1])) {

                begin = l + 1;

            } else if ((l > 0) &&
                    (k - l < a2.length) &&
                    (a1[l - 1] > a2[k - 1])) {

                /**
                 * This is the case where we can discard
                 * a[l-1] from the set of k smallest numbers
                 */
                end = l;

            } else {

                /**
                 * We found our answer since both inequalities were
                 * false
                 */
                begin = l;
                break;
            }
        }

        if (begin == 0) {
            return a2[k - 1];
        } else if (begin == k) {
            return a1[k - 1];
        } else {
            return Math.max(a1[begin - 1], a2[k - begin - 1]);
        }
    }
}
Hrishikesh Mishra
fonte
1

Aqui está a minha solução em java. Tentaremos otimizá-lo ainda mais

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

    }
}

Isso é inspirado em Algo em um vídeo maravilhoso do youtube

M Sach
fonte
1

Link para a complexidade do código (log (n) + log (m))

Link para o código (log (n) * log (m))

Implementação da solução (log (n) + log (m))

Eu gostaria de acrescentar minha explicação ao problema. Este é um problema clássico em que temos que usar o fato de que os dois arrays estão classificados. recebemos duas matrizes classificadas arr1 de tamanho sz1 e arr2 de tamanho sz2

a) Vamos supor que

Verificando se k é válido

k é> (sz1 + sz2)

então não podemos encontrar o menor elemento k na união de ambos os arrays classificados ryt Portanto, retorne dados inválidos. b) Agora, se a condição acima for falsa e tivermos um valor válido e viável de k,

Gerenciando Casos Extremos

Vamos anexar ambos os arrays por valores -infinity na frente e valores + infinito no final para cobrir os casos extremos de k = 1,2 ek = (sz1 + sz2-1), (sz1 + sz2) etc.

Agora, ambas as matrizes têm tamanho (sz1 + 2) e (sz2 + 2), respectivamente

Algoritmo Principal

Agora, faremos uma pesquisa binária em arr1. Faremos uma pesquisa binária em arr1 procurando um índice i, startIndex <= i <= endIndex

de modo que se encontrarmos o índice j correspondente em arr2 usando a restrição {(i + j) = k}, então se

if (arr2 [j-1] <arr1 [i] <arr2 [j]) , então arr1 [i] é o k-ésimo menor (Caso 1)

else if (arr1 [i-1] <arr2 [j] <arr1 [i]) , então arr2 [i] é o k-ésimo menor (Caso 2)

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

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

Uma vez que sabemos que o k - ésimo menor elemento tem (k-1) elementos menores que ele na união de ambas as matrizes ryt? Assim,

No Caso 1 , o que fizemos, que há um total de (k-1) elementos menores em arr1 [i] porque os elementos menores que arr1 [i] na matriz arr1 são em número i-1 do que sabemos (arr2 [ j-1] <arr1 [i] <arr2 [j]) e o número de elementos menores que arr1 [i] em arr2 é j-1 porque j é encontrado usando (i-1) + (j-1) = (k -1) Portanto, o menor elemento k será arr1 [i]

Mas a resposta nem sempre pode vir do primeiro array, isto é, arr1, então verificamos o caso 2, que também satisfaz de forma semelhante ao caso 1 porque (i-1) + (j-1) = (k-1). Agora, se tivermos (arr1 [i-1] <arr2 [j] <arr1 [i]), temos um total de k-1 elementos menores que arr2 [j] na união de ambas as matrizes, portanto é o k-ésimo menor elemento.

No caso 3 , para formar qualquer um dos casos 1 ou 2, precisamos incrementar i e j serão encontrados de acordo com a restrição {(i + j) = k} ou seja, na busca binária, mova para a parte direita, ou seja, faça startIndex = middleIndex

No caso 4 , para formá-lo em qualquer um dos casos 1 ou 2, precisamos decrementar i e j serão encontrados de acordo com a restrição {(i + j) = k} ou seja, na busca binária, mova para a parte esquerda, ou seja, faça endIndex = middleIndex .

Agora, como decidir startIndex e endIndex no início da pesquisa binária em arr1 com startindex = 1 e endIndex = ??. Precisamos decidir.

Se k> sz1, endIndex = (sz1 + 1), caso contrário endIndex = k;

Porque se k é maior que o tamanho do primeiro array, podemos ter que fazer uma busca binária em todo o array arr1, caso contrário, só precisamos pegar os primeiros k elementos dele porque sz1-k elementos nunca podem contribuir no cálculo do k-ésimo menor.

CÓDIGO mostrado abaixo

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

}

Para solução de complexidade (log (n) * log (m))

Perdi a vantagem do fato de que para cada i o j pode ser encontrado usando a restrição {(i-1) + (j-1) = (k-1)} Portanto, para cada ii foi aplicando ainda mais a pesquisa binária na segunda matriz para encontrar j tal que arr2 [j] <= arr1 [i]. Portanto, esta solução pode ser otimizada ainda mais

Vinayak Sangar
fonte
1

Basicamente, por meio dessa abordagem, você pode descartar k / 2 elementos em cada etapa. O K mudará recursivamente de k => k / 2 => k / 4 => ... até atingir 1. Portanto, a complexidade do tempo é O (logk)

Em k = 1, obtemos a menor das duas matrizes.

O código a seguir está em JAVA. Observe que estamos subtraindo 1 (-1) no código dos índices porque o índice do array Java começa em 0 e não em 1, por exemplo. k = 3 é representado pelo elemento no 2º índice de uma matriz.

private int kthElement(int[] arr1, int[] arr2, int k) {
        if (k < 1 || k > (arr1.length + arr2.length))
            return -1;
        return helper(arr1, 0, arr1.length - 1, arr2, 0, arr2.length - 1, k);
    }


private int helper(int[] arr1, int low1, int high1, int[] arr2, int low2, int high2, int k) {
    if (low1 > high1) {
        return arr2[low2 + k - 1];
    } else if (low2 > high2) {
        return arr1[low1 + k - 1];
    }
    if (k == 1) {
        return Math.min(arr1[low1], arr2[low2]);
    }
    int i = Math.min(low1 + k / 2, high1 + 1);
    int j = Math.min(low2 + k / 2, high2 + 1);
    if (arr1[i - 1] > arr2[j - 1]) {
        return helper(arr1, low1, high1, arr2, j, high2, k - (j - low2));
    } else {
        return helper(arr1, i, high1, arr2, low2, high2, k - (i - low1));
    }
}
FaaduBaalak
fonte
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;
}

A complexidade de tempo é O (log (min (n, m)))

HeadAndTail
fonte
1

A maioria das respostas que encontrei aqui se concentra em ambos os arrays. embora seja bom, mas é mais difícil de implementar, pois há muitos casos extremos que precisamos cuidar. Além disso, a maioria das implementações são recursivas, o que adiciona a complexidade do espaço da pilha de recursão. Portanto, em vez de focar nos dois arrays, decidi focar apenas no array menor e fazer a pesquisa binária apenas no array menor e ajustar o ponteiro para o segundo array com base no valor do ponteiro do primeiro Array. Pela implementação a seguir, temos a complexidade de O(log(min(n,m))com a complexidade do O(1)espaço.

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

Temos um intervalo de [low, high]array for ae estreitamos esse intervalo à medida que avançamos no algoritmo. sizeAmostra quantos itens de kitens são da matriz ae deriva do valor de lowe high. sizeBé a mesma definição, exceto que calculamos o valor de forma que sizeA+sizeB=k. Com base nos valores dessas duas bordas, concluímos que devemos estender para o lado direito na matriz aou encolher para o lado esquerdo. se preso na mesma posição que significa que encontramos a solução e vamos devolver o máximo de valores na posição de sizeA-1partir ae sizeB-1de b.

Arnold
fonte
0

Verifique este código.

import math
def findkthsmallest():

    A=[1,5,10,22,30,35,75,125,150,175,200]
    B=[15,16,20,22,25,30,100,155,160,170]
    lM=0
    lN=0
    hM=len(A)-1
    hN=len(B)-1
    k=17

    while True:
        if k==1:
            return min(A[lM],B[lN])


        cM=hM-lM+1
        cN=hN-lN+1
        tmp = cM/float(cM+cN)
        iM=int(math.ceil(tmp*k))
        iN=k-iM
        iM=lM+iM-1
        iN=lN+iN-1
        if A[iM] >= B[iN]:
            if iN == hN or A[iM] < B[iN+1]:
                return A[iM]
            else:
                k = k - (iN-lN+1)
                lN=iN+1
                hM=iM-1
        if B[iN] >= A[iM]:
            if iM == hM or B[iN] < A[iM+1]:
                return B[iN]
            else:
                k = k - (iM-lM+1)
                lM=iM+1
                hN=iN-1
        if hM < lM:
            return B[lN+k-1]
        if hN < lN:
            return A[lM+k-1]

if __name__ == '__main__':
    print findkthsmallest();
Anantha Krishnan
fonte
Fornecer explicação
Abhijit Sarkar
0

Abaixo do código C # para encontrar o k-ésimo menor elemento na união de duas matrizes classificadas. Complexidade de tempo: O (logk)

        public static int findKthSmallestElement1(int[] A, int startA, int endA, int[] B, int startB, int endB, int k)
        {
            int n = endA - startA;
            int m = endB - startB;

            if (n <= 0)
                return B[startB + k - 1];
            if (m <= 0)
                return A[startA + k - 1];
            if (k == 1)
                return A[startA] < B[startB] ? A[startA] : B[startB];

            int midA = (startA + endA) / 2;
            int midB = (startB + endB) / 2;

            if (A[midA] <= B[midB])
            {
                if (n / 2 + m / 2 + 1 >= k)
                    return findKthSmallestElement1(A, startA, endA, B, startB, midB, k);
                else
                    return findKthSmallestElement1(A, midA + 1, endA, B, startB, endB, k - n / 2 - 1);
            }
            else
            {
                if (n / 2 + m / 2 + 1 >= k)
                    return findKthSmallestElement1(A, startA, midA, B, startB, endB, k);
                else
                    return findKthSmallestElement1(A, startA, endA, B, midB + 1, endB, k - m / 2 - 1);

            }
        }
Piyush Patel
fonte
não há bug, testei meu código antes de postar no SO
Piyush Patel
1
Obrigado sammy333, atualizei o código. agora está funcionando
Piyush Patel
(Não calcule a midApartir de endAif k < n. Verifique se há matrizes curtas, começando com return B[startB + k - 1];.)
barba cinza