Interrogazioni su un albero binario di ricerca Search(S,k) – dato un insieme S ed un valore chiave k restituisce un puntatore x ad un elemento in S tale che key[x] = k Maximum(S) – dato un insieme S su cui è definita una relazione dordine totale, restituisce il puntatore allelemento di S con chiave più grande Minimum(S) – dato un insieme S su cui è definita una relazione dordine totale, restituisce lelemento di S con chiave più piccola Successor(S,x) – dato un insieme S su cui è definita una relazione dordine totale, e dato un puntatore x ad un elemento di S, restituisce il successivo elemento più grande in S (o NIL se x punta allelemento massimo di S). Predecessor(S,x) - dato un insieme S su cui è definita una relazione dordine totale, e dato un puntatore x ad un elemento di S, restituisce il successivo elemento più piccolo in S (o NIL se x punta allelemento minimo di S). Interrogazioni – Operazioni che restituiscono informazioni su un insieme dinamico S. Non modificano linsieme.
Ricerca in un ABR Tree-search(x,k) If (x=NIL) o (k = key[x]) then return x If (k < key[x]) then Tree-Search(left[x],k) else Tree-Search(right[x],k) Iterative-Tree-search(x,k) While (x NIL) e (k key[x]) do if k < key[x] then x left[x] else x right[x] Return x Qual è la complessità delloperazione di ricerca in un ABR ? T(n) = O(h) h = Altezza dellalbero
Confronto con il metodo dei dimezzamenti successivi Ipotizziamo che l albero sia completo – Supponiamo di eseguire Tree-search(root(T),7). Supponiamo di avere implementato il dizionario con un array A ordinato. Eseguiamo ricercaBinariaIter(A,7) Per le proprietà dellABR, se lABR fosse completo, allora la chiave di un nodo v sarebbe lelemento mediano nellinsieme ordinato delle chiavi associate allinsieme di nodi costituiti dal sottoalbero sinistro di v, da v, e dal sottoalbero destro di v Ad ogni discesa di livello, dimezzerei lo spazio di ricerca!
Notare … Non è detto che un ABR sia completo. Dipende da come lo abbiamo costruito. Ad esempio, i seguenti 2 ABR rappresentano lo stesso insieme di elementi: Ma anche questo è un ABR Notare: T search (n) = O(h) in entrambi i casi Però: ABR completo h = (log(n)) ABR linearizzato h = (n) 2
Minimo e Massimo Tree-minimum(x) while (left[x] NIL) do x left[x] Return[x] Tree-maximum(x) while (right[x] NIL) do x right[x] Return[x] Complessità - T(n) = O(h)h = altezza dellalbero Tree-minimum(root(T)) Tree-maximum(x)
Successore e predecessore Tree-successor(x) If (right[x] NIL) then return Tree-Minimum(right[x]) y p[x] While (y NIL) e (x=right[y]) do x y y p[y] Return y Cerco il min dellABR con root=right[x] Cerco lantenato più prossimo di x il cui figlio sinistro è la radice del sottoalbero che contiene x Complessità T(n) = O(h) Tree-predecessor(x) è perfettamente simmetrica
In conclusione: Le operazioni di interrogazione Search, Minimum, Maximum, Successor e Predecessor possono essere eseguite in un albero binario di ricerca di altezza h in un tempo O(h).
Operazioni di modifica su un albero binario di ricerca Insert(S,z) – inserisce in S lelemento puntato da z Delete(S,k) - rimuove da S loggetto con chiave k Per semplicità, dirò nodo o elemento intendendo puntatore al nodo e puntatore allelemento, rispettivamente. Operazioni di modifica – operazioni su un insieme dinamico S che modificano le proprietà dellinsieme.
Inserimento Possiamo sempre inserire un nuovo elemento z come foglia. Per decidere la posizione del nuovo elemento possiamo usare la seguente strategia: - discendiamo tutto lalbero, partendo dalla radice. In corrispondenza di ogni nodo x, andiamo a sinistra se key[z] < key[x], andiamo a destra altrimenti Supponiamo di voler inserire un nodo i cui campi sono inizializzati a: Key[z] = 8 p[z] = NIL Left[z] = NIL Right[z] = NIL Se seguo questo schema lelemento z viene posizionato nella posizione giusta. Infatti, per costruzione, ogni antenato di z si ritrova z nel giusto sottoalbero. 8
Tree-insert(T,z) y NIL x root[T] While (x NIL) do y x if (key[z] < key[x]) then x left[x] else x right[x] p[z] y If (y NIL) then if (key[z] < key[y]) then left[y] z else right[y] z else root[T] z Inserimento Cerchiamo la posizione giusta per una foglia con chiave pari a key[z]. y punterà al padre di z Aggiorniamo i puntatori del nodo y Se y=NIL, lalbero contiene solo il nodo z. Il nodo z è allora la radice dellalbero. Non è necessario aggiornare I puntatori left[z] e right[z] Aggiorniamo il puntatore p[z] Complessità T(n) = O(h) h = altezza dellABR N.B. Operazioni di inserimento successive possono linearizzare lalbero.
Cancellazione Sia z (se esiste) il nodo con chiave k. Ci sono 3 situazioni possibili: 1) Cancellazione di un nodo z privo di figli - Modifichiamo il padre di z (p[z]) in modo che non punti più a z 2) Cancellazione di un nodo z con un unico figlio - Estraiamo il nodo z creando un collegamento tra il padre di z (p[z]) ed il figlio di z 3) Cancellazione di un nodo z avente due figli. Estraiamo il successore y di z e sostituiamo il contenuto di z con il contenuto di y
Cancellazione Tree-Delete(T,k) z=Tree-search(root[T],k) If (z NIL) then If (left[z]=NIL) o (right[z]=NIL) then y z else y Tree-Successor(z) If (left[y] NIL) then x left[y] else x right[y] If (x NIL) then p[x] p[y] If (p[y] = NIL) then root[T] x else if (y = left[p[y]]) then left[p[y]] x else right[p[y]] x If (y z) then key[z] key[y] 3 5
Nodo senza figli Tree-Delete(T,k) z=Tree-search(root[T],k) If (z NIL) then If (left[z]=NIL) o (right[z]=NIL) then y z else y Tree-Successor(z) If (left[y] NIL) then x left[y] else x right[y] cioè x=NIL If (x NIL) then p[x] p[y] If (p[y] = NIL) Se il nodo z era la radice, allora then root[T] x root[T] = NIL else if (y = left[p[y]]) altrimenti then left[p[y]] x sostituisce il puntatore a z con NIL else right[p[y]] x If (y z) then key[z] key[y] 5 NIL T(n) = O(h)
Nodo con un figlio Tree-Delete(T,k) z=Tree-search(root[T],k) If (z NIL) then If (left[z]=NIL) o (right[z]=NIL) then y z else y Tree-Successor(z) If (left[y] NIL) then x left[y] x = puntatore allunico figlio di z else x right[y] If (x NIL) then p[x] p[y] p[x] = puntatore al padre di z If (p[y] = NIL) se z era la radice then root[T] x la nuova radice è il figlio di z else if (y = left[p[y]]) altrimenti then left[p[y]] x crea connessione tra il padre di z else right[p[y]] x ed il figlio di z If (y z) then key[z] key[y] 5 T(n) = O(h)
Nodo con due figli Tree-Delete(T,k) z=Tree-search(root[T],k) If (z NIL) then If (left[z]=NIL) o (right[z]=NIL) then y z else y Tree-Successor(z) y = successore di z If (left[y] NIL) then x left[y] else x right[y] x = figlio destro del succes. di z If (x NIL) N.B. – il figlio ds di y potrebbe non esistere then p[x] p[y] il padre di x diventa il padre di y (cancello y) If (p[y] = NIL) N.B. il padre di y non può mai essere NIL then root[T] x else if (y = left[p[y]]) il padre di y deve puntare al nuovo figlio x then left[p[y]] x else right[p[y]] x If (y z) then key[z] key[y] sostituisce la chiave di z con la chiave di y 5 Success. di z z y x T(n) = O(h) 4
In conclusione: Le operazioni di inserimento e di cancellazione di un nodo in un ABR hanno tempo di esecuzione T(n) = O(h) dove h è laltezza dellalbero.