Dividi nell'albero AVL con complessità


9

È possibile implementare l'operazione di suddivisione per alberi AVL con complessità ? Sono interessato a collegamenti ad articoli o informazioni specifiche su questo argomento.O(log⁡n)

L'operazione di divisione divide l'albero AVL in due alberi AVL derivati, in base alla chiave. Uno degli alberi derivati ​​dovrebbe contenere tutti i vertici in cui tutte le chiavi sono inferiori alla chiave originale e la seconda il resto.

So che questo può essere fatto in volta. Ecco un link per l'implementazione con complessità : https://code.google.com/p/self-balancing-avl-tree/O(log2⁡n)O(log2⁡n)

So anche come unire due alberi AVL, in modo tale che le chiavi di un albero siano tutte più piccole delle chiavi dell'altro, nel tempo . Ecco un'implementazione con complessità :O(log⁡n)O(log⁡n)

def Merge(l, r) {
    if (!l || !r) 
        return l ? l : r;
    if (l->h <= r->h)
        r->l = Merge(l, r->l), Rebalance(r);
    else
        l->r = Merge(l->r, r), Rebalance(l);
}

Risposte:


7

Sì, questo è possibile.

Puoi leggerlo in "Strutture dati e algoritmi in una memoria a due livelli" di Ramzi Fadel e Kim Vagn Jakobsen, sezione 3.1.6 , (mirror) o nella libreria standard OCaml, alla funzione "split".

Una delle intuizioni chiave è che la funzione di unione che menzioni è, con una contabilità più attenta, O(h1−h2), dove h1 è l'altezza dell'albero più alto e h2è l'altezza dell'albero più corto. Pertanto, l'unione di un elenco di alberi che hanno solo altezze discendenti o ascendenti costaO(hmax−hmin), poiché la somma telescopi .


-1

Cerchiamo di definire una funzione split(T,v)che prende in un albero, Te un valore di scissione a, v. Supponiamo che ciascun nodo dell'albero memorizzi il suo figlio sinistro e il figlio destro oltre al valore su quel nodo. Utilizzare il seguente algoritmo:

  1. Per prima cosa controlliamo se l'albero di input è semplicemente una foglia o no.

  2. Se Tnon è una foglia, confronta il valore del suo nodo radice v'con v.

  3. Se v' < vquindi chiama in modo ricorsivo la divisione sulla sottostruttura sinistra. Memorizzare i valori della chiamata ricorsiva come L'(albero a sinistra restituito), R'(albero a destra restituito) e r(tipo di opzione indicato se il valore è vstato trovato o meno). Costruisci il nuovo albero a destra newR = Node(R',v',R)e torna indietro (L',r,newR).

  4. Altrimenti, se v' > vpoi chiama in modo ricorsivo split sulla sottostruttura destra. Memorizzare i valori della chiamata ricorsiva come L'(albero a sinistra restituito), R'(albero a destra restituito) e r(tipo di opzione indicato se il valore è vstato trovato o meno). Costruisci il nuovo albero a sinistra newL = Node(L',v',L)e torna indietro (newL,r,R').

  5. Altrimenti se v' = v, ritorna L, SOME(v), R.

  6. Se Tè una foglia, dobbiamo aver raggiunto la radice dell'albero senza trovare il valore di input v a cui dividere. Ritorna che non sei riuscito a trovare la foglia tornando indietro NONE.

Perché questo logaritmico? Bene, attraversi sempre e solo un percorso radice-foglia dell'albero, al massimo. Possiamo ricostruire facilmente nodi in tempo costante poiché stiamo semplicemente riassegnando riferimenti (in un linguaggio imperativo) o riassegnando valori che richiedono un tempo costante per generare (in un linguaggio funzionale ).O(log⁡n)O(log⁡n)

Ecco il codice corrispondente per l'algoritmo. È scritto in SML, ma sarei disposto a chiarire cosa significhi qualcosa nei commenti.

fun split(T,v) = case T of Leaf => (Leaf, NONE, Leaf) | Node(L,v,R) => case compare(v, v') of LESS => let val (L',r,R') = split(L,k) in (L',r,Node(R',r,R)) end | GREATER => let val (L',r,R') = split(R,k) in (Node(L',v',L),r,R') end | EQUAL => (L, SOME(v), R)

Vedi questo documento per maggiori dettagli. Fornisce una spiegazione più approfondita di quanto sopra.


Questo non risolve le condizioni del saldo AVL.
— jbapple,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.