Heap, heap indiretti e code di priorità

Documenti analoghi
Informatica 3. LEZIONE 16: Heap - Codifica di Huffmann. Modulo 1: Heap e code di priorità Modulo 2: Esempio applicativo: codifica di Huffmann

Code a priorità Una coda a priorità è una struttura dati astratta che permette di rappresentare un insieme di elementi su cui è definita una

Heap e code di priorità

Algoritmi e Strutture Dati

Algoritmi e Strutture Dati

Un heap binario è un albero binario con le seguenti caratteristiche:

ADT Coda con priorità

Spesso sono definite anche le seguenti operazioni:

Dato un insieme S di n elementi totalmente ordinato, l'algoritmo di ordinamento detto HeapSort ha le seguenti caratteristiche:

Problemi di ordinamento

Algoritmi (9 CFU) (A.A ) Heap e Algoritmo HeapSort. Prof. V. Cutello Algoritmi 1

heap concetti ed applicazioni

Heap e Code di Priorità

heap heap heap? max- e min-heap concetti ed applicazioni heap = catasta condizione di heap non è una struttura ordinata

Gli heap. Sommario. Algoritmi e Programmazione Avanzata. Fulvio CORNO - Matteo SONZA REORDA Dip. Automatica e Informatica Politecnico di Torino

Gli heap. Sommario. Fulvio CORNO - Matteo SONZA REORDA Dip. Automatica e Informatica Politecnico di Torino

Code a priorità. Progettazione di Algoritmi Matricole congrue a 1. Docente: Annalisa De Bonis

Alberi binari e alberi binari di ricerca

Alberi ed Alberi Binari

Alberi binari e alberi binari di ricerca

Alberi binari e alberi binari di ricerca

Il tipo astratto coda con priorità: specifiche sintattiche e semantiche. Realizzazioni.

Laboratorio di Algoritmi e Strutture Dati. Aniello Murano. people.na.infn.it/~murano/ Murano Aniello - Lab. di ASD Terza Lezione

Algoritmi Greedy. Tecniche Algoritmiche: tecnica greedy (o golosa) Un esempio

Heap, heapsort e code a priorità. Paolo Camurati Dip. Automatica e Informatica Politecnico di Torino

Heap Ordinamento e code di priorità. Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9

Algoritmi e Strutture di Dati

Dati e Algoritmi I (Pietracaprina) Esercizi su Priority Queue e Heap

Introduzione. Liste. Strutture ricorsive (2) Strutture ricorsive (1) DD p KP p

Albero binario: Ogni nodo ha zero, uno, o due successori (ordinati)

Alberi ed Alberi Binari di Ricerca

Alberi binari di ricerca

Alberi. Strutture dati: Alberi. Alberi: Alcuni concetti. Alberi: definizione ricorsiva. Alberi: Una prima realizzazione. Alberi: prima Realizzazione

Algoritmi e Strutture Dati

Introduzione agli algoritmi Prova di esame del 19/9/2016 Prof.sse E. Fachini - R. Petreschi. Parte prima

Esercizi su strutture dati

In questa lezione. Heap binario heapsort. [CLRS10] cap. 6, par Prof. E. Fachini - Intr. Alg.

INFORMATICA 3 Prof.ssa Sara Comai

Le Code. Maurizio Palesi

Algoritmi e Strutture Dati. Capitolo 3 Strutture dati elementari

Esercitazione 8. Corso di Tecniche di programmazione. Laurea in Ingegneria Informatica

In questa lezione. Costruire un max-heap. [CLRS01] cap. 6 par Prof. E. Fachini - Intr. Alg.

Laboratorio di Algoritmi e Strutture Dati. Code con Priorità

Il vettore e la lista concatenate (o catena) sono due esempi di strutture interne.

Esercizio 1 (6 punti)

ESERCIZI SUGLI HEAP BINOMIALI (CAPITOLO 20) Catalano Pietro 56/100592

Algoritmi e Strutture di Dati

LE STRUTTURE DATI DINAMICHE: GLI ALBERI. Cosimo Laneve

Algoritmi e strutture dati

PROGRAMMAZIONE II canale A-D luglio 2008 TRACCIA DI SOLUZIONE

Algoritmi e Strutture Dati. HeapSort

Algoritmi e Strutture Dati. Capitolo 3 Strutture dati elementari

Implementazione dell albero binario in linguaggio C++

Lezione 15 programmazione in Java. Nicola Drago Dipartimento di Informatica Università di Verona

Prof. E. Occhiuto INFORMATICA 242AA a.a. 2010/11 pag. 1

In questa lezione Strutture dati elementari: Pila Coda Loro uso nella costruzione di algoritmi.

Code con priorità. Moreno Marzolla Dip. di Scienze dell'informazione Università di Bologna.

Tipi di dato e Strutture dati elementari

Esercizi Capitolo 10 - Code con priorità e insiemi disgiunti

Insert sort. Considero il primo elemento a 1. cerco il minimo tra gli elementi 3...N. scambio il minimo trovato con il primo e- lemento

Heap scenario. Ho un insieme dinamico di oggetti, ciascuno identificato con una priorità. (la priorità è semplicemente un numero);

Algoritmi e Strutture Dati

Esercitazione 3. Heapsort

Heap binomiali. Oltre alle precedenti operazioni fondamentali degli heap riunibili, sugli heap binomiali definiremo anche le due ulteriori operazioni:

In questa lezione Alberi binari di ricerca: la cancellazione

Laboratorio di algoritmi e strutture dati

Informatica 3. Informatica 3. LEZIONE 14: Alberi binari: introduzione. Lezione 14 - Modulo 1. Definizioni. Introduzione. Definizioni e proprietà

Algoritmi e strutture dati 16 Dicembre 2004 Canali A L e M Z Cognome Nome Matricola

5. Quinta esercitazione autoguidata: liste semplici

Algoritmi e Strutture Dati

Esercizi Capitolo 10 - Code con priorità e insiemi disgiunti

Laboratorio di Python

Algoritmi e Strutture Dati

Algoritmi e Strutture Dati & Laboratorio di Algoritmi e Programmazione

Implementazione ADT: Alberi

In questa lezione. Code di priorità. [CLRS01] cap. 6 da pag. 114 a pag Prof. E. Fachini - Intr. Alg. lunedì 17 settembre 2012

Albero binario. Alberi binari (introduzione) Terminologia. Alberi di ricerca binaria (BST)

Laboratorio di Algoritmi e Strutture Dati

Note per la Lezione 4 Ugo Vaccaro

Alberi. Gli alberi sono una generalizzazione delle liste che consente di modellare delle strutture gerarchiche come questa: Largo. Fosco.

Esercitazione 5 Alberi Binari di Ricerca

Esercizi su programmazione ricorsiva 3

Esercizio 1. E vero che in un AVL il minimo si trova in una foglia o nel penultimo livello? FB = -1. livello 0 FB = -1. livello 1 FB = -1.

CORSO DI PROGRAMMAZIONE

Inserimento in una lista ordinata

ADT Dizionario. Ordered search table. Supponiamo che sia definita una relazione d'ordine totale sulle chiavi del dizionario D:

Alberi. Gli alberi sono una generalizzazione delle liste che consente di modellare delle strutture gerarchiche come questa: Largo. Fosco.

liste ogni nodo ha un successore, tranne l ultimo della lista che ne ha zero; alberi binari ogni nodo ha zero, uno oppure due figli

Alberi e alberi binari I Un albero è un caso particolare di grafo

PROGRAMMAZIONE 1 e LABORATORIO (A,B) - a.a Prova scritta del 10/01/2012 SOLUZIONI PROPOSTE

Algoritmi di ordinamento: Array e ricorsione

Esercizi di Algoritmi e Strutture Dati

Alberi e alberi binari I Un albero è un caso particolare di grafo

ESERCITAZIONI DI INTRODUZIONE AGLI ALGORITMI (A.A. 08/09)

Algoritmi e Strutture Dati. HeapSort

IL CONCETTO DI LISTA!

Esercitazione 11. Liste semplici

Liste concatenate e allocazione dinamica

Transcript:

Heap, heap indiretti e code di priorità Paolo Boldi 15 marzo 02 1 Introduzione Uno heap (letteralmente: mucchio) è (almeno idealmente) un albero binario i cui nodi contengono dei dati, ciascuno caratterizzato da un campo chiave, che soddisfa le seguenti proprietà (vedi Fig. 1): 1. tutte le foglie dello heap hanno altezza (cioè distanza dalla radice) h o h 1 per un valore opportuno di h; 2. le foglie di altezza h 1 (se ce ne sono) stanno a destra delle foglie di altezza h; 3. per ogni nodo, la chiave del nodo è minore o uguale della chiave dei figli. Conseguenza della definizione, naturalmente, è che la radice ha chiave minima. 60 25 Figura 1: Esempio di heap 65 1

2 Rappresentazione mediante vettore Di solito, per memorizzare uno heap, non si utilizza una struttura dinamica ma un semplice vettore. Più precisamente, uno heap con N nodi si memorizza in un vettore 1 a 1 a N in cui il nodo a i ha come figlio sinistro il nodo a 2i e come figlio destro il nodo a 2i 1 (vedi Fig. 2, dove si mostra la rappresentazione mediante vettore dello heap in Fig. 1). Notate che: un nodo di indice i tale che 2i 1 N ha entrambi i figli; l unico (eventuale) nodo che ha un solo figlio (il figlio sinistro) ha indice i tale che 2i 1 N e 2i N; gli altri nodi sono foglie. In altri termini: i nodi con indici 2 1 N 1 2 hanno due figli; i nodi con indice maggiore di N 2 sono foglie; se N è pari, c è un nodo (quello di indice N 2) con un solo figlio. I ndi ce 1 2 3 4 5 6 7 8 9 12 Contenuto 60 25 65 Figura 2: Rappresentazione dello heap di Fig. 1 mediante un vettore 3 Come si cambia il contenuto di un nodo Supponiamo di avere uno heap (rappresentato mediante un vettore) e di aver modificato il contenuto (e la chiave) di un certo nodo a i. In generale, questo dà luogo a una violazione delle proprietà dello heap. In primo luogo, può accadere che il padre di a i si trovi ora ad avere una chiave maggiore della chiave di a i. Bisogna quindi far risalire il nodo modificato sull albero fino a quando siamo sicuri che il padre di a i abbia una chiave minore o uguale a quella di a i. 1 In questa nota si assume che il vettore abbia indici che partono da 1. 2 La notazione x significa x arrotondato per difetto. 2

Può viceversa succedere che a i non abbia chiave minore o uguale di entrambi i figli. Per sistemare le cose, bisognerà scambiare il suo contenuto con quello del figlio avente chiave minore, e far scendere il nodo lungo l albero. 60 25 65 8 8 25 65 60 25 65 60 25 65 60 8 8 Figura 3: Ricostruzione dello heap dopo un inserimento La seguente procedura risistema le cose come indicato. Per generalità, la procedura assume che lo heap si trovi nel frammento di vettore a l a r, e che l elemento modificato sia a i. Alla procedura viene passato l indice i del nodo modificato, e due indici l e r che corrispondono al primo e all ultimo indice dello heap considerato. void heapify (int i, int l, int r) { 3

DATA temp=a[i]; while (i>=l && temp.key<a[i/2].key) { a[i]=a[i/2]; i/=2; a[i]=temp; while (i<=r/2) { child=2*i; if (child+1<=r && a[child+1].key<a[child].key) child++; if (temp.key<=a[child].key) break; a[i]=a[child]; i=child; a[i]=temp; Naturalmente, si può utilizzare la procedura heapify anche per ripristinare le proprietà di uno heap dopo che si è inserito un nuovo elemento: è sufficiente inserire l elemento nuovo in ultima posizione del vettore (cioè come una nuova foglia dello heap) e poi invocare la procedura perché il nuovo elemento trovi la sua posizione. In Fig. 3 mostriamo come si verifica l inserimento di un nuovo elemento di chiave 8 nello heap di Fig. 1: il nuovo nodo risale l albero (primo ciclo while) o ridiscende lungo l albero (secondo ciclo while) fino a trovare la sua posizione naturale. 4 Aggiungere/togliere elementi da uno heap; costruzione di uno heap da zero Disponendo della procedura heapify possiamo facilmente disporre di uno strumento per aggiungere/togliere elementi da uno heap: 1. per aggiungere un elemento allo heap a 1 a N possiamo procedere come segue: a[++n]=nuovo elemento; heapify(n,1,n); 4

2. per eliminare l elemento i-esimo dallo heap a 1 a N possiamo procedere come segue: a[i]=a[n--]; heapify(i,1,n); Se si dispone già di un vettore a 1 a N e lo si vuole rendere uno heap si può o utilizzare ripetutamente la procedura di inserimento di un elemento indicata sopra, oppure alternativamente costruire lo heap dal basso come segue: for (i=n/2;i>0;i--) heapify(i,i,n); Il vantaggio di quest ultima soluzione è che richiede, anche nel caso peggiore, un numero di confronti proporzionale a N (mentre l idea di inserire un elemento dopo un altro dà luogo a un numero di confronti proporzionale a N log N). 5 Heap indiretti In qualche caso si dispone di un vettore b 1 b N che si vuole gestire come uno heap ma senza modificare l ordine degli elementi nel vettore. In tal caso, conviene usare due vettori ausiliari p e q, con il seguente significato: q i dice in quale posizione dello heap si trova l elemento b i ; p j dà l indice (nel vettore b) dell elemento che si trova nella posizione j dello heap. Ovviamente dovrà valere sempre che q p i! i e p q j! j. Inizialmente p i " q i # i per ogni indice i. Tutte le operazioni sullo heap viste continuano a funzionare, con le seguenti modifiche: le occorrenze di a[x] vanno sostituite con a[p[x]]; ogni assegnamento del tipo a[i]=a[j] va sostituito con q[i]=q[j]; p[q[i]]=j; 5

6 Un applicazione: Code con priorità Mostreremo come utilizzare la struttura di heap indiretto per realizzare una struttura dati molto comune, detta coda con priorità. Supponiamo di avere un vettore a 1 a n contenente dei dati (generici), dichiarato come segue: typedef... DATA; DATA a[maxn]; Vogliamo costruire una struttura (che chiameremo coda con priorità) che consenta di memorizzare elementi presi dal vettore, associando a ciascuno un valore di priorità (un numero intero o reale), e che renda disponibili le seguenti operazioni: enqueue(i,a) (inserisce nella coda l elemento a i con priorità a); isinqueue(i) (dice se l elemento a i è presente nella coda); dequeue() (restituisce l indice dell elemento con priorità minima fra quelli presenti nella coda, e lo elimina dalla coda); changepriority(i,b) (cambia la priorità dell elemento a i, che deve essere presente nella coda, e la setta a b). Stiamo assumendo, per motivi di generalità, che la priorità degli elementi sia un dato non contenuto nel vettore, e che fa parte della struttura stessa della coda. Spesso, in realtà, la priorità è parte del vettore di partenza a di partenza: in tal caso, il vettore di priorità non serve, e tutte le procedure non hanno veramente bisogno del parametro di priorità perché possono leggerlo dal vettore. Procediamo utilizzando uno heap indiretto. Dichiariamo perciò i vettori p e q e la costante N per memorizzare il numero di elementi presenti in coda: int p[maxn],q[maxn],priority[maxn],n; void init() { int i; N=0; for (i=1;i<=n;i++) p[i]=q[i]=-1; 6

Realizziamo ora le procedure: /* Ricostruisce lo heap dopo che l elemento che nello heap si trova in pos. i ha cambiato priorita. Considera solo lo heap dall indice l all indice r. */ void heapify (int j, int l, int r) { int tempq,tempp,child; tempq=q[j]; /* Salvo la posizione di j nello heap... */ tempp=j; /*...e salvo j */ /* Faccio risalire l elemento */ while (j>l && priority[q[j]]<priority[q[j/2]]) { q[j]=q[j/2]; p[q[j]]=j/2; j/=2; /* Lo faccio scendere */ while (j<=r/2) { child=2*j; if (child+1<=r && priority[q[child+1]]<priority[q[child]]) child++; if (priority[q[j]]<=priority[q[child]]) break; q[j]=q[child]; p[q[j]]=child; j=child; /* Ora rimetto i valori salvati nella posizione finale */ q[j]=tempq; p[q[j]]=tempp; /* Guarda se l elemento i e nella coda. */ int isinqueue(int i) { return (q[i]!=-1); /* Accoda l elemento i con priorita a. */ 7

void enqueue(int i, int a) { if (isinqueue(i)) return; /* Elemento gia in coda */ /* Metto l elemento in fondo alla coda */ q[i]=++n; p[n]=i; priority[i]=a; /* Heapifico in modo che l elemento inserito trovi la sua posizione corretta */ heapify(n,1,n); /* Restituisce e toglie dalla coda l elemento di priorita minima. */ int dequeue() { int res; if (N==0) return -1; /* Coda vuota */ /* Tolgo l elemento in testa */ res=q[1]; /* Al suo posto metto l ultima foglia */ q[1]=q[n]; p[q[1]]=n--; /* Heapifico in modo che l elemento messo in posizione 1 ritrovi la sua posizione corretta */ heapify(1,1,n); return res; /* Cambia la priorita dell elemento i a b. */ void changepriority(int i, int b) { if (!isinqueue(i)) return; /* Elemento non in coda */ /* Modifico la priorita e heapifico */ priority[q[i]]=b; heapify(i,1,n); 8