1 © Alberto Montresor Algoritmi e Strutture Dati Capitolo 10

Algoritmi e Strutture Dati
Capitolo 10 - Code con priorità e insiemi disgiunti
Alberto Montresor
Università di Trento
This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike License. To view a
copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/2.5/ or send a letter to Creative
Commons, 543 Howard Street, 5th Floor, San Francisco, California, 94105, USA.
© Alberto Montresor
1
Introduzione
Strutture dati viste finora
✦
Sequenze, insiemi e dizionari
✦
Strutture “speciali”
✦
Le operazioni base (inserimento, cancellazione, lettura, etc.) possono non essere
sufficienti: a volte è necessario inventarsene di nuove
✦
Se non tutte le operazioni sono necessarie, è possibile realizzare strutture dati più
efficienti, “specializzate” per particolare compiti
✦
© Alberto Montresor
2
Code con priorità
L'idea
✦
Una struttura dati – detta heap – che mantiene dati in modo parzialmente ordinato
✦
Utilizzazioni
✦
Code con priorità
✦
Gli oggetti vengono estratti dalla coda in base alla loro priorità
✦
Heapsort
✦
Algoritmo di ordinamento
✦
Costo computazionale: O(n log n)
✦
Ordinamento sul posto
✦
© Alberto Montresor
3
Alberi binari
Albero binario perfetto
✦
Tutte le foglie hanno la stessa profondità h
✦
Nodi interni hanno grado 2
✦
Un albero perfetto
✦
Ha altezza ⎣log N⎦
✦
Altezza h → #nodi= 2h+1-1
✦
Albero binario completo
✦
Tutte le foglie hanno profondità h o h-1
✦
Tutti i nodi a livello h sono “accatastati” a sinistra
✦
Tutti i nodi interni hanno grado 2, eccetto al più uno
✦
© Alberto Montresor
4
Alberi binari heap
Max heap
Un albero binario completo è un
albero max-heap sse
✦
Ad ogni nodo i viene associato
un valore A[i]
16
✦
A[p(i)] ≥ A[i]
✦
Un albero binario completo è un
albero min-heap sse
14
10
✦
8
7
9
3
Ad ogni nodo i viene associato
un valore A[i]
✦
A[p(i)] ≤ A[i]
2 4
1
✦
Le definizioni e gli algoritmi di max-heap sono simmetrici rispetto a min-heap
✦
© Alberto Montresor
5
Alcune informazioni sugli alberi heap
Un albero heap non impone alcuna relazione
di ordinamento fra i figli di un nodo
✦
Un albero heap è un ordinamento parziale
✦
16
14
10
Riflessivo: Ogni nodo è ≥ di se stesso
✦
{a,b}
Antisimmetrico: se n≥m e m≥n, allora m=n
✦
Transitivo: se n≥m e m≥r, allora n≥r
✦
{a}
{b}
Ordinamenti parziali
✦
Utili per modellare gerarchie complesse o
mantenere informazioni parziali
✦
Nozione più debole di un ordinamento totale...
✦
Ma più semplice da costruire
✦
© Alberto Montresor
6
Array heap
E' possibile rappresentare un albero binario heap tramite un
array heap (oltre che tramite puntatori)
✦
A[1]
16
Come è fatto?
✦
Array A[1..n]
✦
A[2]
14
A[3]
10
A[4] A[5]
A[6] A[7]
Come è organizzato?
✦
A[1] contiene la radice
✦
8
p(i) = ⎣i/2⎦
✦
7
9
3
l(i) = 2i
✦
A[8] 2 4 1 A[10]
A[9]
r(i) = 2i+1
✦
n = 10
16 14 10 8 7 9 3 2 4
© Alberto Montresor
1
7
Array heap
Ricapitolando:
✦
Array max-heap: A[i] ≥ A[2i], A[i] ≥ A[2i+1]
✦
Procedure per gestire heap
✦
maxHeapRestore - O(log n)
✦
Un algoritmo che mantiene la proprietà di max-heap
✦
heapBuild - O(n)
✦
Un algoritmo che costruisce un max-heap da zero
✦
heapsort - O(n log n)
✦
Ordina sul posto un array
✦
© Alberto Montresor
www.xkcd.com
8
maxHeapRestore()
Input
✦
Un array A e un indice i
✦
Gli alberi binari con radici l(i) e r(i) sono max-heap
✦
E' possibile che A[i] sia minore di A[l(i)] o A[r(i)]
✦
In altre parole: il sottoalbero con radice i può non essere un
max-heap
✦
Scopo
✦
Ripristinare la proprietà di max-heap sul sottoalbero con radice i
✦
Facendo “scendere” l'elemento A[i] nell'array
✦
© Alberto Montresor
9
maxHeapRestore()
© Alberto Montresor
10
maxHeapRestore()
Domanda: Esempio di funzionamento
✦
5, 15, 12, 9, 10, 7, 4, 3, 6, 8, 2
✦
E' un array max-heap?
✦
E' possibile applicare maxHeapRestore() alla radice?
✦
Domanda:
✦
Qual è la complessità in tempo di maxHeapRestore()?
✦
Domanda
✦
Dimostrare la correttezza di maxHeapRestore()
✦
© Alberto Montresor
11
heapBuild()
Principio generale
✦
Sia A[1..n] un array da ordinare
✦
Tutti i nodi A[⎣n/2⎦+1 ... n] sono foglie dell'albero e quindi heap di un elemento da
cui iniziare
✦
La procedura heapBuild() attraversa i restanti nodi dell'albero ed esegue
maxHeapRestore()
✦
© Alberto Montresor
12
heapBuild()
Domanda: Esempio di funzionamento
✦
14
14, 45, 28, 34, 15, 20, 12, 30, 21, 25, 16, 22
✦
Domanda: Correttezza
✦
Esprimere un'invariante di ciclo
✦
45
28
Dimostrare la sua correttezza
✦
Domanda – Complessità
✦
Qual è la complessità di
heapBuild()?
34
15
20
12
✦
30
© Alberto Montresor
21
25
16
22
13
Complessità
Le operazioni maxHeapRestore() vengono eseguite su heap di altezza variabile
✦
Viene eseguito ⎡n/2⎤ volte su heap di altezza 0
✦
Viene eseguito ⎡n/4⎤ volte su heap di altezza 1
✦
Viene eseguito ⎡n/8⎤ volte su heap di altezza 2
✦
...Viene eseguito ⎡n/2h+1⎤ volte su heap di altezza h
✦
Da cui si deduce
✦
Ricordate:
✦
Per x < 1
✦
© Alberto Montresor
14
Ordinamento tramite heap
Intuizione
✦
Il primo elemento dello heap è sempre il massimo
✦
Andrebbe collocato nell'ultima posizione
✦
L'elemento in ultima posizione? In testa!
✦
Chiama maxHeapRestore() per ripristinare la situazione
✦
© Alberto Montresor
15
heapsort()
Domanda: Esempio di funzionamento
✦
45
14, 45, 28, 34, 15, 20, 12, 30, 21, 25, 16, 22
✦
Domanda: Correttezza
✦
Esprimere un'invariante di ciclo
34
✦
28
Dimostrare la sua correttezza
✦
Domanda: Complessità
✦
30
25
22
12
Qual è la complessità di
heapsort()?
✦
14
© Alberto Montresor
21
15
16
20
16
Esercizi
Esercizio
✦
Dimostrare che uno heap con n nodi ha altezza θ(log n)
✦
Esercizio
✦
Scrivere un'implementazione iterativa di maxHeapRestore()
(in pseudo-codice)
✦
Esercizio
✦
Dimostrare che il tempo di esecuzione nel caso peggiore di
maxHeapRestore() è Ω(log n)
✦
Esercizio
✦
Implementare heapsort() nel vostro linguaggio preferito
✦
© Alberto Montresor
17
Code con priorità
Coda con priorità
✦
Una struttura dati che serve a mantenere un insieme S di
elementi x, ciascuno con un valore associato di priorità
✦
© Alberto Montresor
18
Specifica
© Alberto Montresor
19
Code con priorità

Esempio di utilizzo

Simulatore event-driven

Ad ogni evento è associato un timestamp di esecuzione

Ogni evento può generare nuovi eventi, con timestamp arbitrari


Una coda con min-priorità può essere utilizzata per eseguire gli eventi in ordine
di timestamp
Esempi di organizzazione
p 3 ev
p 7 ev
© Alberto Montresor
evento
p 5 ev
p
p
p
p
p
p
3
4
5
6
8
7
ev
ev
ev
ev
ev
ev
20
Code con priorità
© Alberto Montresor
21
Inserimento
© Alberto Montresor
22
Code con priorità - minHeapRestore()
© Alberto Montresor
23
Rimozione del minimo e riduzione priorità
© Alberto Montresor
24
Code con priorità
Esercizio
✦
Qual è la complessità delle operazioni sulle code con priorità?
✦
Esercizio
✦
Scrivere lo pseudocodice di una versione heapBuild() basata sulla insert()
✦
Le due procedure creano lo stesso heap? Dimostrare o
produrre un esempio contrario
✦
Qual è la complessità di questa versione di heapBuild()?
✦
Esercizio
✦
L'azione di delete(PriorityItem x) cancella l'elemento x dallo heap. Scrivete una
versione di delete() che operi in tempo O(log n).
✦
© Alberto Montresor
25
Struttura dati per insiemi disgiunti
Motivazioni
✦
In alcune applicazioni siamo interessati a gestire insiemi disgiunti
di oggetti
✦
Esempio: componenti di un grafo
✦
Operazioni fondamentali:
✦
unire più insiemi
✦
identificare l'insieme a cui appartiene un oggetto
✦
Struttura dati
✦
Una collezione S = { S1, S2, ..., Sn } di insiemi dinamici disgiunti
✦
Ogni insieme è identificato da un rappresentante univoco
✦
© Alberto Montresor
26
Scelta del rappresentante
Il rappresentante può essere un qualsiasi membro dell’insieme Si
✦
Operazioni di ricerca del rappresentante su uno stesso insieme devono restituire
sempre lo stesso oggetto
✦
Solo in caso di unione con altro insieme il rappresentante può
cambiare
✦
Il rappresentante può essere un elemento specifico dell’insieme
✦
Si devono definire le caratteristiche degli insiemi e una regola
per caratterizzare il rappresentante
✦
Esempio: l’elemento più piccolo/grande di un insieme
✦
© Alberto Montresor
27
Primitive degli insiemi disgiunti (Merge-Find)
© Alberto Montresor
28
Esempio
mfset(6)
1
2
3
4
5
6
merge(1,2)
1 1, 2 2
3
4
5
6
merge(3,4)
1 1, 2 2
3 3,4 4
5
6
merge(5,6)
1 1, 2 2
3 3,4 4
5 5,6 6
merge(1,3)
1
1,
2 2, 3, 34
merge(1,5)
1
2
© Alberto Montresor
4
1, 2,
3 3, 4, 45, 6
5 5,6 6
5
6
29
Esempio: come utilizzare Merge-Find
Determinare le componenti connesse di un grafo non orientato dinamico
✦
Algoritmo
✦
Si inizia con componenti connesse costituite da un unico vertice
✦
L'operazione merge(find(u), find(v)) viene eseguita per ogni arco (u,v)
✦
Alla fine, avremo l'insieme delle componenti connesse
✦
Complessità
✦
Costo: O(n) + m operazioni merge()
✦
Questo algoritmo è interessante per
la capacità di gestire grafi dinamici
(in cui gli archi vengono aggiunti)
✦
© Alberto Montresor
30
Implementazione di Union-Find
Algoritmi elementari:
✦
Realizzazione basata su liste
✦
find():
✦
O(1);
merge():
O(n)
Realizzazione basata su alberi
✦
merge():
✦
O(1);
find():
O(n)
Algoritmi basati su euristiche di bilanciamento
✦
Realizzazione basata su liste + Euristica sul peso
✦
Realizzazione basata su alberi + Euristica sul rango
✦
Algoritmi finale
✦
Alberi + rango + compressione dei percorsi
✦
© Alberto Montresor
31
Realizzazione basata su liste
Ogni insieme viene rappresentato con una lista concatenata
✦
Il primo oggetto di una lista è il rappresentante dell’insieme
✦
Ogni elemento nella lista contiene:
✦
un oggetto
✦
un puntatore all’elemento successivo
✦
un puntatore al rappresentante
✦
#
head
tail
© Alberto Montresor
c
h
e
b
Può essere
visto come
un albero di
altezza 1
32
Realizzazione basata su liste
L’operazione find(x) richiede tempo O(1)
✦
Si restituisce il rappresentante di x
✦
L’operazione merge(x,y) è più complessa:
✦
Si “appende” la lista che contiene y alla lista che contiene x
✦
I puntatori ai rappresentanti della lista “appesa” devono essere modificati
✦
Costo nel caso pessimo per n operazioni: O(n2)
✦
Costo ammortizzato: O(n)
✦
merge(2,1), merge(3,1), merge(4,1), etc.
✦
© Alberto Montresor
33
QuickFind: Esempio - merge(h, g)
c
h
f
© Alberto Montresor
e
g
b
d
z
f
g
d
z
c
h
e
b
34
Realizzazione basata su alberi (foreste)
Implementazione basata su foresta
✦
Si rappresenta ogni insieme tramite un albero radicato
✦
Ogni nodo dell'albero contiene
✦
l'oggetto
✦
un puntatore al padre
✦
f
c
Il rappresentante è la radice dell'albero
✦
La radice ha come padre un puntatore
a se stessa
✦
Nota: generalmente migliore, non più
veloce delle liste nel caso pessimo
✦
© Alberto Montresor
h
b
e
d
g
35
Realizzazione basata su alberi
Operazioni e costo
✦
find(x)
✦Risale la lista dei padri di x fino a trovare la radice e restituisce la radice come
oggetto rappresentante
✦
Costo: O(n) nel caso pessimo
✦
merge(x,y)
✦
Appende l'albero radicato in y ad x
✦
Costo: O(1)
✦
© Alberto Montresor
36
Realizzazione basata su alberi: Esempio – merge(c, f)
f
c
h
b
e
c
d
h
g
b
e
f
d
g
© Alberto Montresor
37
Realizzazione basata su alberi: Esempio – merge(c, f)
f
c
e
d
g
c
e
f
d
g
© Alberto Montresor
38
Considerazioni
Quando usare....
✦
Realizzazione basata su liste?
✦
Quando le merge() sono rare e le find() frequenti
✦
Realizzazione basate su alberi?
✦
Quando le find() sono rare e le merge() frequenti
✦
E' importante sapere che esistono tecniche euristiche che permettono di migliorare
questi risultati:
✦
Fino a operazioni in tempo ammortizzato O(1) per tutte le utilizzazioni pratiche
✦
© Alberto Montresor
39
Realizzazione basata su liste: Euristica peso
Una strategia per diminuire il costo dell’operazione merge():
✦
Memorizzare l’informazione sulla lunghezza della lista
✦
Appendere la lista più corta a quella più lunga
✦
La lunghezza della lista può essere mantenuta in tempo O(1)
✦
4
2
c
© Alberto Montresor
h
e
b
f
g
40
Realizzazione basata su liste: Euristica peso
Una strategia per diminuire il costo dell’operazione merge():
✦
Memorizzare l’informazione sulla lunghezza della lista
✦
Appendere la lista più corta a quella più lunga
✦
La lunghezza della lista può essere mantenuta in tempo O(1)
✦
6
c
© Alberto Montresor
h
e
b
f
g
41
Realizzazione basata su alberi: Euristica sul rango
Ogni nodo mantiene informazioni sul proprio rango
✦
il rango rango[x] di un nodo x è il numero di archi del cammino più lungo fra x e
una foglia sua discendente
✦
rango ≡ altezza del sottoalbero associato al nodo
✦
Unione di due alberi
con rango diverso
✦
+
c
h
e
b
f
d
Unione di due alberi
con rango uguale
✦
=
c
h
b
+
c
e
f
h
d
b
e
f
=
c
d
h
g
b
e
f
d
g
© Alberto Montresor
42
Compressione dei cammini
Compressione dei cammini: idea
✦
Utilizzata durante le operazioni find(x)
✦
L'albero viene modificato in modo che ricerche successive di x possano completare
in O(1)
✦
Esempio: operazione find(f)
✦
b
e
d
b
f
d
e
f
© Alberto Montresor
43
Compressione dei cammini + rango
Quando si utilizzano entrambe le euristiche:
✦
Il rango non è più l'altezza del nodo ...
✦
...ma il limite superiore all'altezza del nodo
✦
In altre parole:
✦
Le operazioni di compressione dei cammini NON riducono il rango
✦
Sarebbe troppo complesso mantenere le informazioni di altezza
corrette
✦
In ogni caso, non è necessario
✦
© Alberto Montresor
44
Realizzazione basata su alberi + rango + compressione dei cammini
© Alberto Montresor
45
Complessità



Valutiamo ora la complessità di:

Liste + Euristica sul peso

Alberi + Euristica sul rango

Alberi + Euristica sul rango + Compressione dei cammini
Alla lavagna!
Ne segue che tutte queste implementazioni (e in particolare l'ultima) sono
ampiamente utilizzabili in pratica
© Alberto Montresor
46