Salta al contenuto
Note per Studenti Multi-way search tree e alberi (2,4)

Multi-way search tree e alberi (2,4)

In questa pagina 5

Problema degli ABR (vedi Alberi binari di ricercaAlbero binario di ricerca come albero binario proprio con entry nei nodi interni e foglie vuote; inorder crescente; TreeSearch; get, put e remove (due casi, con predecessore inorder) in Theta(h); altezza fino a n-1; esempi di inserimenti; alberi aumentati con campi size e max e algoritmi di conteggio e interrogazione in O(h).Alberi binari di ricerca →): get, put, remove costano Θ(h)\Theta(h) ma hh può essere proporzionale a nn. Due rimedi: ristrutturare l'albero quando lo sbilanciamento supera una soglia (AVL, alberi rosso-neri, non trattati) oppure rendere i nodi più capienti e usare la capienza per assorbire ingressi e uscite di entry: gli alberi (2,4). L'idea dei nodi capienti è la stessa dei B-tree usati nei database e nei file system: un nodo corrisponde a un blocco letto con un solo accesso a un disco (Memoria esterna - dischi, RAID e memorie otticheDisco magnetico (piatti, facce, tracce, settori, cilindri); tempo di accesso come somma di posizionamento, latenza rotazionale e trasferimento, con formule ed esempio; algoritmi di scheduling FCFS, SSTF e ascensore; livelli RAID da 0 a 6; SSD; CD, DVD e nastri.Memoria esterna - dischi, RAID e memorie ottiche →, Gerarchia di memoria e principio di localitàCaratteristiche delle memorie (posizione, capacità, unità di trasferimento, metodo di accesso, prestazioni, volatilità); compromesso costo-capacità-velocità e gerarchia a livelli; località temporale e spaziale con esempi; hit, miss e tempo medio di accesso con calcolo svolto.Gerarchia di memoria e principio di località →), quindi conviene avere pochi livelli e molte chiavi per nodo.

Multi-way search tree

Definizione (multi-way search tree). Un multi-way search tree (MWS tree) è un albero ordinato (vedi AlberiAlbero radicato (definizione per padre e ricorsiva), terminologia (antenati, discendenti, nodi interni ed esterni, sottoalbero, albero ordinato), profondità, livello, altezza; altezza = massima profondità delle foglie; algoritmi depth e height con costo; somma dei figli = n-1; esempio di algoritmo Omega(n^2) (heightBad).Alberi →) in cui:

  • ogni nodo interno ha ≥2\ge 2 figli;
  • ogni nodo interno con d≥2d \ge 2 figli v1,…,vdv_1, \dots, v_d (dd-nodo) memorizza d−1d - 1 entry (k1,x1),…,(kd−1,xd−1)(k_1, x_1), \dots, (k_{d-1}, x_{d-1}) con k1<k2<⋯<kd−1k_1 < k_2 < \dots < k_{d-1};
  • per 1≤i≤d1 \le i \le d ogni chiave kk del sottoalbero TviT_{v_i} soddisfa ki−1<k<kik_{i-1} < k < k_i (con k0=−∞k_0 = -\infty, kd=+∞k_d = +\infty).

Esempio. Un 33-nodo con chiavi 10<2010<20 ha 33 figli, con chiavi <10<10, in (10,20)(10,20) e >20>20. Il caso d=2d=2 è il nodo dell'ABR (Alberi binari di ricercaAlbero binario di ricerca come albero binario proprio con entry nei nodi interni e foglie vuote; inorder crescente; TreeSearch; get, put e remove (due casi, con predecessore inorder) in Theta(h); altezza fino a n-1; esempi di inserimenti; alberi aumentati con campi size e max e algoritmi di conteggio e interrogazione in O(h).Alberi binari di ricerca →): una chiave e due sottoalberi, uno con chiavi minori e uno con chiavi maggiori. Gli intervalli aperti (ki−1,ki)(k_{i-1},k_i) sono quelli dei intervalli di numeri realiNaturali, interi, razionali e reali, perché la radice di 2 obbliga a passare da Q a R, e gli intervalli.Insiemi numerici (N, Z, Q, R) →: gli dd intervalli (−∞,k1),(k1,k2),…,(kd−1,+∞)(-\infty,k_1),(k_1,k_2),\dots,(k_{d-1},+\infty) coprono tutta la retta tranne le chiavi del nodo, quindi ogni chiave cercata ha esattamente un posto dove andare.

Per convenzione, come negli ABR, le foglie non memorizzano entry. Un ABR è il caso in cui ogni nodo interno è un 2-nodo.

Esempio di 4-nodo: tre chiavi k1<k2<k3k_1 < k_2 < k_3 e quattro figli, con chiavi <k1< k_1 in v1v_1, in (k1,k2)(k_1, k_2) in v2v_2, in (k2,k3)(k_2, k_3) in v3v_3, >k3> k_3 in v4v_4.

Ricerca

Algoritmo MWTreeSearch(k, v)
Input: chiave k, nodo v     Output: nodo di T_v con la chiave k, o la foglia "giusta" per k
if T.isExternal(v) then return v
siano (k1,x1), ..., (k_{d-1}, x_{d-1}) le entry di v
trova i con k_{i-1} < k <= k_i   (k_0 = -infinito, k_d = +infinito)
if k = k_i then return v
else return MWTreeSearch(k, v_i)

get(k): cerca come sopra; se il risultato è una foglia restituisce null, altrimenti la entry in vv con chiave kk. Complessità: il cammino ha lunghezza ≤h\le h e in ogni nodo si cerca tra ≤dmax\le d_{max} chiavi (se le entry di un nodo sono in una mappa secondaria, ad esempio una lista): O(dmax h)O(d_{max} \, h).

Esempio: radice (10,20)(10, 20) con figli (3,7)(3, 7), (14,17)(14, 17), (25,30,35)(25, 30, 35). Cercare 1717: tra 1010 e 2020 si va nel secondo figlio (14,17)(14, 17) e si trova. Cercare 2424: 24>2024 > 20, si scende nel terzo figlio; 24<2524 < 25, si scende nel suo primo figlio, che è una foglia: assente.

Numero di foglie

Proposizione (numero di foglie di un MWS tree). Un MWS tree con nn entry ha n+1n + 1 foglie.

Esempio. Con radice (10,20)(10,20) e figli (3,7)(3,7), (14,17)(14,17), (25,30,35)(25,30,35) le entry sono 99 e le foglie 3+3+4=10=9+13+3+4=10=9+1.

Dimostrazione (induzione sull'altezza hh, vedi Principio di induzionePer dimostrare una proprietà per ogni n basta il passo base e il passo induttivo da n a n+1.Principio di induzione →). Base h=0h = 0: la sola radice è una foglia, n=0n = 0, 11 foglia. Passo: TT di altezza h+1h+1 con radice dd-nodo, sottoalberi T1,…,TdT_1, \dots, T_d con nin_i entry e mim_i foglie, altezze ≤h\le h. Per ipotesi mi=ni+1m_i = n_i + 1. Allora n=(d−1)+∑nin = (d-1) + \sum n_i e m=∑mi=∑(ni+1)=d+∑ni=1+(d−1)+∑ni=n+1m = \sum m_i = \sum (n_i + 1) = d + \sum n_i = 1 + (d - 1) + \sum n_i = n + 1. □\square

(Se tutti i nodi interni fossero dd-nodi, l'albero sarebbe dd-ario e le foglie sarebbero (d−1)⋅(nodi interni)+1(d-1) \cdot (\text{nodi interni}) + 1, con (d−1)⋅(nodi interni)=(d-1) \cdot (\text{nodi interni}) = numero di entry.)

Alberi (2,4)

Definizione (albero (2,4) e altezza). Un albero (2,4) è un MWS tree tale che:

  1. ogni nodo interno è un dd-nodo con 2≤d≤42 \le d \le 4 (cioè ha 11, 22 o 33 entry);
  2. tutte le foglie hanno la stessa profondità.

Proposizione. Un (2,4) con n>0n > 0 entry ha altezza Θ(log⁡n)\Theta(\log n).

Esempio. Con n=1000n=1000 entry l'altezza è tra log⁡41001≈5\log_4 1001\approx5 e log⁡21001≈10\log_2 1001\approx10, cioè Θ(log⁡n)\Theta(\log n).

Dimostrazione. Sia hh l'altezza (= livello delle foglie) e mim_i il numero di nodi al livello ii. Si ha m0=1m_0 = 1 e, poiché ogni nodo interno ha tra 22 e 44 figli e le foglie sono tutte a livello hh, 2i≤mi≤4i2^i \le m_i \le 4^i. In particolare 2h≤mh≤4h2^h \le m_h \le 4^h. Ma mh=n+1m_h = n + 1 (proposizione sulle foglie). Quindi n+1≥2h⇒h≤log⁡2(n+1)n + 1 \ge 2^h \Rightarrow h \le \log_2(n+1) e n+1≤4h⇒h≥12log⁡2(n+1)n + 1 \le 4^h \Rightarrow h \ge \frac12 \log_2(n+1). Dunque h∈Θ(log⁡n)h \in \Theta(\log n). □\square

I passaggi sui logaritmi: n+1≥2hn+1 \ge 2^h si inverte applicando log⁡2\log_2, funzione crescente (Esponenziale e logaritmoLa funzione esponenziale a^x (base positiva diversa da 1) e la sua inversa, il logaritmo in base a, con grafici e proprietà.Esponenziale e logaritmo →), e dà h≤log⁡2(n+1)h \le \log_2(n+1); n+1≤4h=22hn+1 \le 4^h = 2^{2h} dà log⁡2(n+1)≤2h\log_2(n+1) \le 2h, cioè h≥12log⁡2(n+1)h \ge \frac12\log_2(n+1). Il grafico mostra la fascia in cui sta l'altezza di un (2,4) con nn entry: per n=1000n=1000 è tra ≈5\approx5 e ≈10\approx10, mentre un ABR degenere avrebbe altezza 999999.

Grafico interattivo: Altezza di un albero (2,4) con n entry: compresa tra metà di log2(n+1) (tutti 4-nodi) e log2(n+1) (tutti 2-nodi); per n = 1000 tra 5 e 10

Controllo sull'esempio della ricerca (radice (10,20)(10,20) con tre figli): n=9n=9, altezza 22; le foglie sono 10=n+110=n+1 e valgono 22=4≤10≤42=162^2=4\le10\le4^2=16.

Corollario. In un (2,4) con nn entry MWTreeSearch e get costano Θ(log⁡n)\Theta(\log n) (ogni nodo ha ≤3\le 3 chiavi, dmaxd_{max} è costante). Anche put e remove si implementano in Θ(log⁡n)\Theta(\log n); la loro implementazione non è richiesta all'esame ma l'idea è semplice:

  • Inserimento: si cerca la foglia ww e si inserisce la chiave nel nodo vv padre di ww. Se vv diventa un 5-nodo (4 chiavi, overflow) lo si spezza: la terza delle quattro chiavi sale al padre, le prime due restano in un nodo (a 33 figli) e l'ultima forma l'altro (a 22 figli). L'overflow può propagarsi verso la radice; se la radice si spezza, nasce una nuova radice e l'altezza cresce di 11 (tutte le foglie scendono insieme: la condizione 2 resta vera). Esempio: inserendo 2626 nel nodo (25,30,35)(25, 30, 35) si ha (25,26,30,35)(25, 26, 30, 35): si spezza in (25,26)(25, 26) e (35)(35) e 3030 sale nella radice, che diventa (10,20,30)(10, 20, 30).

Perché la condizione 2 resta vera: lo spezzamento non cambia la profondità delle foglie (i nuovi nodi sono allo stesso livello del vecchio) e la salita della chiave aumenta di 11 i figli del padre senza cambiare livelli. Inoltre i due nodi nati dallo spezzamento hanno 33 e 22 figli, quindi rispettano il vincolo 2≤d≤42\le d\le4. Sequenza completa di inserimenti 10,20,30,40,50,60,7010,20,30,40,50,60,70 partendo dall'albero vuoto (una sola foglia):

inserimento albero dopo note
1010 (10)(10) la foglia diventa nodo interno con due foglie
2020 (10,20)(10,20) 3-nodo
3030 (10,20,30)(10,20,30) 4-nodo, ancora valido
4040 (10,20,30,40)(10,20,30,40) →\to radice (30)(30) con figli (10,20)(10,20) e (40)(40) overflow: la terza chiave sale e crea una nuova radice, altezza 1→21\to2
5050 radice (30)(30), figli (10,20)(10,20) e (40,50)(40,50) 50>3050>30, si va a destra
6060 radice (30)(30), figli (10,20)(10,20) e (40,50,60)(40,50,60)
7070 (40,50,60,70)(40,50,60,70) →\to radice (30,60)(30,60), figli (10,20)(10,20), (40,50)(40,50), (70)(70) overflow: sale 6060 nella radice

In ogni istante le foglie sono tutte alla stessa profondità, e l'altezza è passata da 11 a 22 solo quando si è spezzata la radice.

  • Rimozione: una entry di un nodo non terminale si sostituisce con il predecessore inorder, poi si toglie la entry da un nodo con foglie come figli. Se il nodo rimane senza entry (underflow) si fa un trasferimento da un fratello con almeno 33 figli oppure una fusione con un fratello a 22 figli (che può propagare l'underflow in su; se la radice si svuota, l'altezza diminuisce).

Dizionario con alberi di ricerca

Un dizionario (multimap, vedi Mappe e dizionariADT mappa (chiavi distinte) con get, put, remove, keySet/values/entrySet; famiglia mappa/mappa ordinata/dizionario (multimappa); applicazioni; implementazioni semplici ma poco efficienti (lista, array indicizzato dalle chiavi); dizionario realizzato con una mappa di liste.Mappe e dizionari →) si realizza o con una mappa di coppie (k,Lk)(k, L_k), o modificando ABR e (2,4) per ammettere chiavi uguali.

Confronto

Metodo ABR (2,4)
get(k) Θ(h)\Theta(h) Θ(log⁡n)\Theta(\log n)
put(k, v) Θ(h)\Theta(h) Θ(log⁡n)\Theta(\log n)
remove(k) Θ(h)\Theta(h) Θ(log⁡n)\Theta(\log n)
remove(k, v) (dizionario) Θ(s+h)\Theta(s + h) Θ(s+log⁡n)\Theta(s + \log n)

(ss = numero di valori associati alla chiave.) Nell'ABR hh può essere n−1n - 1; nel (2,4) il bilanciamento è garantito dalla struttura.

Errori comuni

  • Dimenticare la condizione "tutte le foglie alla stessa profondità": senza di essa l'altezza non è logaritmica.
  • Contare le foglie come entry: un MWS tree con nn entry ha n+1n+1 foglie vuote.
  • Cercare linearmente i figli come in un ABR: in un dd-nodo si cerca l'intervallo ki−1<k≤kik_{i-1} < k \le k_i tra d−1d - 1 chiavi.
  • Stimare la complessità come Θ(h)\Theta(h) senza specificare h=Θ(log⁡n)h = \Theta(\log n) per i (2,4).

Versione ripasso

Esercizi su questo argomento

Teoria collegata