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

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(log2n)O(log2n)

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(logn)O(logn)

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(h1h2), 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(hmaxhmin), 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(logn)O(logn)

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.