Come ragionare sulla complessità di Mettere k elenchi concatenati ordinati?

1

Sto cercando di capire meglio la notazione di Big O per essere in grado di ragionare in modo molto più chiaro e, quindi, ho bisogno di un feedback sulla mia analisi fornita di seguito per un problema: Unisci elenchi concatenati k-ordinati in uno solo.

Rivendicazione n. 1:

var merge2Lists = function(list1, list2) {
        if (!list1) {
            return list2;
        }
        if (!list2) {
            return list1;
        }

        if (list1.val <= list2.val) {
            list1.next = merge2Lists(list1.next, list2);
            return list1;
        }
        else {
            list2.next = merge2Lists(list2.next, list1);
            return list2;
        }
    };

merge2Lists menzionato sopra è utilizzato da entrambe le soluzioni indicate di seguito. La complessità di questa funzione dovrebbe essere O (n1 + n2) dove n1 e n2 sono rispettivamente numero di elementi in list1 e list2. Oppure possiamo solo dire che nel peggiore dei casi entrambe le liste avranno lo stesso numero di elementi e dovremo esaminarli tutti almeno una volta, quindi possiamo chiamarlo O(n) .

Claim # 2

// Solution: 1
var mergeKLists = function(lists) {
    let len = lists.length, interval = 1, i;
    while (interval < len) {
        for (i = 0; i < len; i += interval*2) {
            lists[i] = merge2Lists(lists[i], lists[i+interval])
        }  
        interval *= 2;
    }
    return (len > 0) ? lists[0]: lists;
}

Dire che ho 10 liste nel mio input che devono essere unite. La mia soluzione precedente prende quelle 10 liste e nella prima iterazione del ciclo while, unisce 10/2 = 5 coppie. Nella prossima iterazione, unisce 10/4 = 3 coppie. Quindi unisce 10/8 = 2 coppie e infine abbiamo la nostra lista ordinata finale negli elenchi [0].

Poiché il numero di coppie da unire viene dimezzato a ogni passaggio, la sua complessità temporale dovrebbe essere log(k) , dove k è il numero di elenchi nell'input. Un altro modo per descriverlo può essere che, se raddoppiamo la dimensione dell'ingresso (cioè da 10 a 20), il numero di operazioni aumentate sarà non doppio, il che significa che non è sicuramente O(n) . Il numero di operazioni può aumentare solo di 1, il che significa che dovrebbe essere log(k) .

Insieme alla rivendicazione 1, la complessità totale dovrebbe diventare: O (n * log k) . Non so come spiegarlo chiaramente però.

La complessità dello spazio dovrebbe essere O(1) dato che la nostra memoria rimane costante rispetto alle dimensioni dell'input.

Richiesta n. 3

// Solution: 2
var mergeKLists = function(lists) {
    let len = lists.length;
    if (!len) return lists;

    while (lists.length > 1) {
        lists.push(merge2Lists(lists[0], lists[1]));
        lists.shift();
        lists.shift();
    }
    return lists[0];
};

Qui mergeKLists sta unendo solo 1 coppia in ogni iterazione del ciclo while. Se abbiamo 10 liste, dopo la prima iterazione avremmo fuso solo 1 coppia. Nella seconda iterazione uniremo un'altra coppia e così via. Quindi sembra che stiamo facendo 10 operazioni in totale. Quindi la sua complessità dovrebbe essere O (k) dove k è il numero di liste in input.

Se raddoppiamo la dimensione dell'input da 10 a 20, il numero di operazioni aumentate sarà 10, il che è in linea con la nostra complessità dedotta di O(k) .

Più sopra stiamo usando shift() funzione 2 volte per scartare gli elenchi non necessari dalla matrice. La complessità di shift () stesso è O (m) dove m è il numero di elementi nell'array spostato. Nel peggiore dei casi, lo spostamento dovrà spostare m elementi a sinistra. Quindi la complessità temporale per questa soluzione diventerà O (k + m) .

Insieme alla rivendicazione 1, la complessità totale dovrebbe diventare: O (n * (k+m)) .

La complessità dello spazio dovrebbe essere O(1) come per ogni nuovo elemento che viene spinto nell'array, scartando gli elementi dall'array in cui lo spazio acquisito non aumenta rispetto all'input.

  1. Queste affermazioni sono corrette? In caso contrario, puoi gentilmente aiutarmi a capire meglio?
  2. Esiste un modo più chiaro per motivo sulle dichiarazioni fatte sopra?
  3. Ho notato che almeno su LeetCode Solution # 2 funzionava a 30 ms più veloce della soluzione n. 1 per lo stesso set di casi di test. Può essere solo un bug di LeetCode o Network Latency, in quanto mi aspettavo che la Soluzione n. 1 fosse eseguita molto più velocemente a causa della sua complessità nel tempo di log (k).
posta Undefined 18.07.2018 - 16:26
fonte

1 risposta

1

RECLAMO 1

La rivendicazione 1 sembra giusta (ma il codice potrebbe essere sostituito in modo semplice se (list2.val < list1.val) con 'else'. Vorrei anche commentare nel codice in qualche luogo che si stanno unendo alle liste in modo intrusivo - cambiando il argomenti in arrivo.

RECLAMO 2:

Questa parte penso che tu abbia ragione, ma ha spiegato male. Quando hai detto "ts time complex dovrebbe essere log (k), dove k è il numero di liste nell'input" Ero molto confuso. Vuoi dire che passi attraverso quel ciclo log (k) volte, ma il tempo per ogni (loop interno) è limitato (da sopra) da O (N) dove N è la SUM delle lunghezze di tutte le liste (dato che tu non fai so su quali elenchi funzionerai)

Inoltre, non ho mai visto dove hai chiaramente definito "n", ma presumo per n intendi SUM n (sub-i).

RECLAMO 3:

L'introduzione di una nuova variabile 'm' qui non è una buona idea. È delimitato dall'alto da 'n' e cambia ogni volta attraverso il ciclo (quindi non è una funzione del problema ma della soluzione), quindi non è un buon candidato per fare analisi.

Hai ragione a guardare attraverso k volte. E se continuiamo ad assumere 'n' è la somma delle lunghezze di tutte le liste, allora n è un limite superiore sulle lunghezze di ciascuno degli argomenti delle due liste da unire.

E se assumiamo che le 'liste' siano implementate usando un vettore (non ha bisogno di essere e sarebbe più performante se fosse anche una lista collegata), allora la complessità totale diventa

O (K * k) (per gli spostamenti dell'array di lunghezza k fatto k volte) PLUS O (k * N) per la k si fonde.

O - O (k * (k + n))

BTW - se usi una lista collegata per 'elenchi' - diventa O (k * n) - perché le operazioni dell'elenco collegato - spingendo in primo piano e spuntando dal fronte - sono tutte O (1).

    
risposta data 18.07.2018 - 17:10
fonte

Leggi altre domande sui tag