Scaricare la presentazione
La presentazione è in caricamento. Aspetta per favore
1
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 or send a letter to Creative Commons, 543 Howard Street, 5th Floor, San Francisco, California, 94105, USA. © Alberto Montresor
2
Strutture dati viste finora Sequenze, insiemi e dizionari
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
3
Gli oggetti vengono estratti dalla coda in base alla loro priorità
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
4
Albero binario perfetto Tutte le foglie hanno la stessa profondità h
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
5
Un albero binario completo è un albero max-heap sse
Alberi binari heap Max heap Un albero binario completo è un albero max-heap sse Ad ogni nodo i viene associato un valore A[i] A[p(i)] ≥ A[i] Un albero binario completo è un albero min-heap sse A[p(i)] ≤ A[i] Le definizioni e gli algoritmi di max-heap sono simmetrici rispetto a min-heap 16 14 10 8 7 9 3 2 4 1 © Alberto Montresor
6
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 Riflessivo: Ogni nodo è ≥ di se stesso Antisimmetrico: se n≥m e m≥n, allora m=n Transitivo: se n≥m e m≥r, allora n≥r Ordinamenti parziali Utili per modellare gerarchie complesse o mantenere informazioni parziali Nozione più debole di un ordinamento totale... Ma più semplice da costruire 16 16 14 14 10 10 {a,b} {a} {b} ∅ © Alberto Montresor
7
Array heap E' possibile rappresentare un albero binario heap tramite un array heap (oltre che tramite puntatori) Come è fatto? Array A[1..n] Come è organizzato? A[1] contiene la radice p(i) = ⎣i/2⎦ l(i) = 2i r(i) = 2i+1 A[1] 16 A[2] A[3] 14 10 A[4] A[5] A[6] A[7] 8 7 9 3 A[8] 2 4 1 A[10] A[9] n = 10 16 14 10 8 7 9 3 2 4 1 © Alberto Montresor
8
www.xkcd.com 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
9
Gli alberi binari con radici l(i) e r(i) sono max-heap
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
10
maxHeapRestore() © Alberto Montresor
11
Domanda: Esempio di funzionamento 5, 15, 12, 9, 10, 7, 4, 3, 6, 8, 2
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
12
Sia A[1..n] un array da ordinare
heapBuild() Principio generale Sia A[1..n] un array da ordinare Tutti i nodi A[⎣n/2⎦ 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
13
Domanda: Esempio di funzionamento
heapBuild() Domanda: Esempio di funzionamento 14, 45, 28, 34, 15, 20, 12, 30, 21, 25, 16, 22 Domanda: Correttezza Esprimere un'invariante di ciclo Dimostrare la sua correttezza Domanda – Complessità Qual è la complessità di heapBuild()? 14 45 28 34 15 20 12 30 21 25 16 22 © Alberto Montresor
14
Viene eseguito ⎡n/2⎤ volte su heap di altezza 0
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
15
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
16
Domanda: Esempio di funzionamento
heapsort() Domanda: Esempio di funzionamento 14, 45, 28, 34, 15, 20, 12, 30, 21, 25, 16, 22 Domanda: Correttezza Esprimere un'invariante di ciclo Dimostrare la sua correttezza Domanda: Complessità Qual è la complessità di heapsort()? 45 34 28 30 25 22 12 14 21 15 16 20 © Alberto Montresor
17
Dimostrare che uno heap con n nodi ha altezza θ(log n)
Esercizi Esercizio Dimostrare che uno heap con n nodi ha altezza θ(log n) Scrivere un'implementazione iterativa di maxHeapRestore() (in pseudo-codice) Dimostrare che il tempo di esecuzione nel caso peggiore di maxHeapRestore() è Ω(log n) Implementare heapsort() nel vostro linguaggio preferito © Alberto Montresor
18
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
19
Specifica © Alberto Montresor
20
Simulatore event-driven
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 3 ev evento p 4 ev p 5 ev p 6 ev p p 7 ev p 8 ev 5 ev p 7 ev © Alberto Montresor
21
Code con priorità © Alberto Montresor
22
Inserimento © Alberto Montresor
23
Code con priorità - minHeapRestore()
© Alberto Montresor
24
Rimozione del minimo e riduzione priorità
© Alberto Montresor
25
Qual è la complessità delle operazioni sulle code con priorità?
Esercizio Qual è la complessità delle operazioni sulle code con priorità? 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()? 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
26
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
27
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
28
Primitive degli insiemi disgiunti (Merge-Find)
© Alberto Montresor
29
Esempio mfset(6) 1 2 3 4 5 6 merge(1,2) 1 2 3 4 5 6 1, 2 merge(3,4) 1
5,6 merge(1,3) 1 2 3 4 5 6 1, 2, 3, 4 5,6 merge(1,5) 1, 2, 3, 4, 5, 6 1 2 3 4 5 6 © Alberto Montresor
30
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
31
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
32
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 Può essere visto come un albero di altezza 1 # head c h e b tail © Alberto Montresor
33
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
34
QuickFind: Esempio - merge(h, g)
b f g d z f g d z c h e b © Alberto Montresor
35
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 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 f c h e d b g © Alberto Montresor
36
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
37
Realizzazione basata su alberi: Esempio – merge(c, f)
h e d h e f d b b g g © Alberto Montresor
38
Realizzazione basata su alberi: Esempio – merge(c, f)
d e f d g g © Alberto Montresor
39
Realizzazione basata su liste?
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
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) 4 2 c h e b f g © Alberto Montresor
41
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 h e b f g © Alberto Montresor
42
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 Unione di due alberi con rango uguale f f c + = c c + = c h e d h e f h e d h e f b b d b g b d g © Alberto Montresor
43
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 f b e d f © Alberto Montresor
44
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
45
Realizzazione basata su alberi + rango + compressione dei cammini
© Alberto Montresor
46
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
Presentazioni simili
© 2024 SlidePlayer.it Inc.
All rights reserved.