In questa lezione Strutture dati elementari: Pila Coda

Похожие документы
Alberi ed Alberi Binari

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.

Esercizi di Algoritmi e Strutture Dati

Heap e code di priorità

Note per la Lezione 4 Ugo Vaccaro

Esercitazione: Implementazione in linguaggio C dell ADT. Stack con l utilizzo. di linked list

LE STRUTTURE DATI DINAMICHE: GLI ALBERI. Cosimo Laneve

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

Liste con sentinella. intlist *createlist(void){ intlist *q = malloc(sizeof(intlist)); if(!q) { exit(-1); } q->next = q->prev = q; return q; }

In questa lezione Alberi binari di ricerca: la cancellazione

Alberi binari di ricerca

Grafi: visita generica

La struttura dati CODA

Laboratorio di Algoritmi e Strutture Dati. Code con Priorità

Esercitazione 6. Alberi binari di ricerca

ADT: Abstract Data Type. Quasi ADT. ADT per collezioni di dati (code generalizzate) 04 I tipi di dati astratti (I parte)

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

Questa soluzione va contemplata quando le lunghezze stimate dalle liste usate sono significativamente maggiori delle dimensioni di un elemento.

Lezione 12 Tabelle Hash

Per semplicità eliminiamo le ripetizioni nell'albero.

Problemi, istanze, soluzioni

Algoritmi e Strutture Dati

Lezione 6 Le pile. Informatica. 28 Aprile 2016

PILE E CODE. Pile (stack):

Fondamenti di Informatica

Alberi Binari di Ricerca

Alberi binari e alberi binari di ricerca

Algoritmi e Strutture di Dati I 1. Algoritmi e Strutture di Dati I Massimo Franceschet francesc

Implementazione ADT: Alberi

Struttura dati astratta Coda

18 - Vettori. Programmazione e analisi di dati Modulo A: Programmazione in Java. Paolo Milazzo

ADT Coda con priorità

Esercizi di Algoritmi e Strutture Dati

Alberi Binari di Ricerca

Laboratorio di Informatica

I Tipi di Dato Astratto

Progettazione di algoritmi

Esempi. non. orientato. orientato

In questa lezione. Heapsort. ordinamento con complessità, nel caso peggiore, O(nlogn) [CLRS01] cap. 6 da pag. 106 a pag. 114

Contenitori: Pile e Code

Algoritmi di Ricerca. Esempi di programmi Java

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

Esercizi Capitolo 11 - Strutture di dati e progettazione di algoritmi

Laboratorio di Informatica. Esercitazione su algoritmi e diagrammi di flusso

Problema del cammino minimo

Sistemi Web per il turismo - lezione 3 -

Array e Oggetti. Corso di Laurea Ingegneria Informatica Fondamenti di Informatica 1. Dispensa 12. A. Miola Dicembre 2006

Analisi asintotica della complessità di tempo degli algoritmi

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

Ordinamenti per confronto: albero di decisione

lezione 9 min-heap binario Heap e Alberi posizionali generali

Un albero completamente bilanciato o pieno (full) alberi completamente sbilanciati. Un albero binario completo

Linguaggi di programmazione - Principi e paradigmi 2/ed Maurizio Gabbrielli, Simone Martini Copyright The McGraw-Hill Companies srl

Un algoritmo realizza una relazione funzionale tra i valori di input e quelli di output

Scope, Memoria e Tabella dei Simboli

Dati e Algoritmi I (Pietracaprina) Esercizi sugli Alberi

4 Le liste collegate 4.0. Le liste collegate. 4 Le liste collegate Rappresentazione di liste 4.1 Rappresentazione di liste

Informatica Generale Andrea Corradini Gli algoritmi e la risoluzione di problemi

Stringhe e allocazione dinamica della memoria

Alberi binari. Esercizi su alberi binari

Algoritmi e Strutture Dati. HeapSort

Da infissa a Postfissa

Esercizi riassuntivi (Fondamenti di Informatica 2 Walter Didimo) Soluzioni

Fondamenti di Informatica. Algoritmi di Ricerca e di Ordinamento

Strutture dati per insiemi disgiunti (union-find)

Informatica 1. Prova di recupero 21 Settembre 2001

19 - Eccezioni. Programmazione e analisi di dati Modulo A: Programmazione in Java. Paolo Milazzo

Programmazione a blocchi. Algobuild Prof. Sergio Roselli

Sviluppo di programmi

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

Programmazione Orientata agli Oggetti. Emilio Di Giacomo e Walter Didimo

Individuazione di sottoproblemi

Транскрипт:

In questa lezione Strutture dati elementari: Pila Coda 1

strutture dati (astratte) Una struttura dati astratti consiste di uno o più insiemi con delle operazioni che li modificano (insiemi dinamici). Le operazioni sono descritte sulla base del loro effetto. Esempio La struttura dati in cui si ha un insieme di elementi con le operazioni di inserimento, che ha come risultato quello di aggiungere una nuova elemento di cancellazione, che ha come effetto quello di eliminare un dato elemento dall insieme, se presente di ricerca di una elemento, che fornisce la possibilità di recuperare un dato elemento nell insieme, se presente, e ne segnala l assenza altrimenti è chiamata dizionario (dictionary) 2

strutture dati (astratte): i dati Gli elementi su cui si vogliono eseguire queste operazioni spesso sono complessi, sono oggetti rappresentati in memoria con strutture con molti campi. Assumeremo che un campo in particolare, la chiave (key), è quella su cui effettuiamo le nostre operazioni. Se infatti assumiamo che le chiavi sono tutte diverse, allora abbiamo a che fare con un insieme. Gli altri dati dell oggetto sono chiamati dati satellite (satellite data). 3

strutture dati lineari Si tratta di collezioni in cui gli elementi sono organizzati in sequenza, come in una lista Python. Gli elementi si possono inserire o prelevare solo a una delle due estremità della lista. Questo tipo di restrizioni caratterizza il tipo di dato astratto, per esempio in una pila gli elementi sono prelevati e aggiunti ad un solo estremo della sequenza. 4

Pila (stack) Una pila è un insieme dinamico di dati in cui la cancellazione è vincolata a un elemento particolare: l ultimo inserito. solo l elemento in cima è accessibile!! L ultimo ad essere inserito è il primo ad essere estratto: Last In First Out. 5

Operazioni Le operazioni fondamentali (di interfaccia) sono: push che inserisce un elemento in cima (top) alla pila pop che estrae l elemento in cima alla pila Esempio: Ogni browser ha uno stack, che viene usato quando si vuole tornere indietro all ultima pagina visitata prima della corrente. Ogni volta che si apre una nuova pagina, il suo indirizzo va nello stack (push). Quando si torna indietro si estrae dalla pila l ultimo indirizzo inserito (pop). Volendo si possono riesaminare tutte le pagine aperte nell ordine contrario a quello di apertura. 6

Operazioni - 2 Oltre alle operazioni fondamentali push, che inserisce un elemento in cima (top) alla pila e pop, che estrae l elemento in cima alla pila, si definiscono le operazioni pilavuota che dà true se la pila è vuota, false altrimenti top che dà l elemento in cima, senza estrarlo 7

Implementazione Per utilizzare una struttura dati astratta è necessario fornire un implementazione, cioè scegliere come rappresentare i dati (la struttura dati concreta) e definire gli algoritmi per i calcoli relativi alle operazioni. In programmazione il concetto di interfaccia viene usato per nascondere l implementazione e consentire l uso delle funzionalità in modo indipendente dall implementazione, esattamente come avviene per i tipi di dati elementari come i numeri interi. 8

Strutture dati astratte e concrete In generale il processo è il seguente: 1. si definisce la struttura dati astratta 2. si sceglie la struttura dati concreta con cui rappresentare i dati in memoria 3. si definiscono gli algoritmi che realizzano gli obiettivi specificati nelle funzioni. Array e liste concatenate sono le strutture dati concrete più usate nell implementazione di una pila 9

Il problema delle parentesi ben bilanciate Consideriamo l insieme delle parentesi ben bilanciate, cioè di stringhe di parentesi, di tre tipi, in cui ogni parentesi aperta ha la sua corrispondente chiusa e tutte sono propriamente annidate. Per esempio le seguenti sono ben bilanciate: ([({})[]]) (({[]})()) ([((()){[]})()]) mentre le seguenti NO (([({)[]])} ())) ([]{}(() 10

Uso uno stack? Consideriamo il problema di stabilire se una stringa in input è una sequenza di parentesi ben bilanciata. Per definizione una sequenza è ben bilanciata se ogni aperta ha una sua chiusa corrispondente e se sono correttamente annidate. Guardando a questo esempio x = ([({})[]]) leggendo da sinistra a destra ogni volta l ultima aperta sospesa è la prima a essere accoppiata. Dunque serve uno stack! 11

l algoritmo in bozza Alg Verificaparentesi(x) input: x è un espressione aritmetica, prec: le parentesi che occorrono in x sono del tipo: (,[,{ postc: restituisce vero se le parentesi in x sono ben bilanciata, falso altrimenti Si impilano le aperte e si eliminano dalla cima dello stack le aperte in corrispondenza di ogni chiusa della stesso tipo. Quindi quando si considera un carattere c: se c è una parentesi aperta viene impilato se c è una parentesi chiusa, viene confrontato con l aperta in cima alla pila e se risultano dello stesso tipo si esegue una pop 12

Esempio di uso dello stack: l algoritmo Alg Verificaparentesi(x) crea una pila vuota; leggi il primo carattere di x e mettilo in c finchè ci sono caratteri da leggere in x se c non è una parentesi, passa al carattere successivo se c è una parentesi aperta, esegui una push di c nella pila altrimenti %c è una parentesi chiusa se la pila è vuota, restituisci false altrimenti %la pila non è vuota, salva l elemento in cima in d e esegui una pop se la parentesi aperta d e quella chiusa c sono dello stesso tipo, leggi il successivo carattere e mettilo in c altrimenti restituisci false se la pila è vuota, restituisci true altrimenti restituisci false La pila in ogni passo di esecuzione del ciclo contiene le parentesi aperte non accoppiate con una chiusa fino a quel passo. Ogni parentesi aperta viene inserita ed estratta dalla pila una sola volta, quindi T(n) = Θ(n), inoltre l algoritmo usa uno spazio lineare per la coda 13

Esempio di esecuzione x = ( [ ] { { [ ] } ( ) } ) 0 1 2 3 4 5 6 7 8 9 10 11 x[0] = ( va conservata, quindi push di ( x[1] = [ va conservata, quindi push di [ x[2] = ] chiude x[1] : pop x[3], x[4] e x[5] vanno conservate, [ quindi si eseguono i relativi push x[6] = ] chiude x[5] : pop ( ( ( [ { { ( { { ( { ( ( { ( { ( x[7] = } chiude x[4] : pop x[8] = ( va conservata, quindi push di ( L evoluzione della pila x[9] = ) chiude x[8] : pop x[10] = } chiude x[3] : pop x[11] = ) chiude x[0] : pop 14

Coda Una coda è un insieme dinamico di dati in cui la cancellazione è vincolata a un elemento particolare: il primo inserito. solo il primo elemento inserito è accessibile Il primo ad essere inserito è il primo ad essere estratto: First In First Out 15

Le operazioni Ogni coda ha un inizio, la testa (head) della coda, e una fine (tail). Le operazioni sono: Accoda (Enqueue): inserisce un elemento alla fine della coda, (tail) estrazione (Dequeue): cancella l elemento in testa alla coda, (head) front:dà, senza estrarlo l elemento in testa alla coda codavuota: dà true se la coda è vuota, false altrimenti 16

APPLICAZIONE DELLA CODA: VISITA PER LIVELLI DI UN ALBERO BINARIO 1 Livello 0 2 7 Livello 1 3 6 9 Livello 2 4 Livello 3 Si vuole visitare i nodi su ogni livello da sinistra a destra e in ordine discendente di livello. Risultato visita: 1 27 369 4 17

VISITA PER LIVELLI DI UN ALBERO BINARIO 1 Livello 0 2 7 Livello 1 3 5 6 9 Livello 2 4 8 Livello 3 10 Livello 4 Risultato visita: 1 2 7 3 5 6 9 4 8 10 Osserviamo che se per esempio abbiamo visitato 6 il prossimo da visitare è suo fratello ma il successivo è il figlio del primo nodo sul suo livello. Allora se man mano che si visita un livello si tiene memoria degli eventuali figli, cioè dei nodi del livello successivo, il primo memorizzato è il primo da estrarre. Quindi serve una coda! 18

Cosa fare in un passo generico Alg VisitaPerLivelli(T) //T è un albero binario postc: stampa la sequenza dei nodi di T per livelli da sinistra a destra. In un generico passo, potremmo trovarci alla fine di un livello i e nella necessità di stampare i nodi del livello successivo, partendo da sinistra. Quindi bisogna che quei nodi del livello i +1 siano stati accodati nell ordine da sinistra verso destra, e l unica possibilità di farlo è nel quando si stampa il loro padre. Quindi l azione da fare sul nodo in esame è la sua stampa e l accodamento dei suoi figli. Infine un nodo stampato non serve più e quindi deve essere tolto dalla coda. 1 2 7 3 6 9 4 Nodi nella coda prima della stampa di 7 Nodi nella coda dopo la stampa di 7 Q= Q= 7 3 3 6 9 19

Cosa fare in un passo generico 1 Una situazione del tutto analoga si verifica se consideriamo un passo intermedio in cui ci troviamo su un generico nodo di un livello. Infatti quel nodo va stampato e tolto dalla coda, che deve contenere i restanti nodi di quel livello, ancora da stampare, e anche i figli dei nodi già stampati. Inoltre bisogna accodare i figli del nodo tolto e stampato. 3 4 8 Nodi nella coda prima della stampa di 5 Q= 2 7 5 5 6 9 4 6 9 Nodi nella coda dopo la stampa di 5 Q= 6 9 4 8 20

Visita per livelli: l'algoritmo la coda contiene i tutti i nodi di un livello oppure i nodi dal k-simo fino all ultimo di un livello e tutti i nodi del livello successivo figli dei nodi alla sinistra del k-simo. In ogni caso i nodi sono nell ordine da sinistra verso destra. Alg VisitaPerLivelli(T) //T è un albero binario postc: stampa la sequenza dei nodi di T per livelli da sinistra a destra. Q è una coda inizialmente vuota accoda in Q il nodo radice, se l'albero è non vuoto while la coda non è vuota do y = dequeue(q) stampa y if il figlio sinistro,x, di y è presente then Enqueue(Q,x) if se il figlio destro,x, di y è presente then Enqueue(Q,x) N.B. L algoritmo è formulato in modo da prescindere dalla rappresentazione in memoria di T, nel senso che la struttura che rappresenta un nodo potrebbe contenere, oltre al campo chiave, due o tre campi puntatore. 21

Visita per livelli: esempio di esecuzione 1 Contenuto coda all inizio: 2 7 1 Nodo estratto e stampato: 1 3 6 9 2 7 4 Nodo estratto e stampato: 2 7 3 Nodo estratto e stampato: 7 3 6 9 Nodo estratto e stampato: 3 6 9 4 Nodo estratto e stampato: 6 9 4 Nodo estratto e stampato: 9 4 Nodo estratto e stampato: 4 22

Visita per livelli: analisi Alg VisitaPerLivelli(T) //T è un albero binario postc: stampa la sequenza dei nodi di T per livelli da sinistra a destra. Q è una coda inizialmente vuota accoda in Q il nodo radice, se l'albero è non vuoto while la coda non è vuota do y = dequeue(q) stampa y if il figlio sinistro,x, è presente then Enqueue(Q,x) if se il figlio destro,x, è presente then Enqueue(Q,x) Ogni nodo viene inserito ed estratto dalla coda una sola volta, quindi T(n) = Θ(n) 23

Esercizi Es. 10.1-6. Mostrate come implementare una pila usando due code. Bisogna definire le operazioni Top,push e pop usando enqueue e dequeue. Si analizzi la complessità asintotica delle operazioni push e pop in questa nuova implementazione. Es. 10.1-7. Mostrate come implementare una coda usando due pile. Bisogna definire le operazioni enqueue e dequeue usando push e pop. Si analizzi la complessità asintotica delle operazioni enqueue e dequeue in questa nuova implementazione. 24

Calcolare lo Span di un array di numeri Dato un array X, lo span S[i] di X[i] è il massimo numero di elementi consecutivi X[j] immediatamente precedenti e tali che X[j] X[i] quindi il minimo span di X[i] è 1, perché lui stesso conta Esempio: X= 6 3 9 4 5 8 0 1 2 3 4 5 S= 1 1 3 1 2 3 0 1 2 3 4 5 In altri termini S[i] = i - j se X[j] > X[i] e X[j+1],,X[i-1],X[i] X[i] 25

Un Algoritmo per calcolare lo span Algorithm span1(x) Input:array X di n interi, con indici da 0 Output: array S di span di X crea un nuovo array di n elementi for i = 0 to n-1 s=1 while (s i and X[i-s] X[i]): s = s+1 S[i] = s return S Nel caso peggiore per ogni i esamino tutti i precedenti. Allora il ciclo esterno comporta l esecuzione di 1 + 2 + + (n 1) istruzioni Algoritmo span1 ha complessità O(n 2 ) 26

Analisi del problema S[i] = s se X[i-s],,X[i-1], X[i] X[i] e X[i-(s+1)]>X[i] X[m] X[k] X[j] X[i] X[k-1] X[m+1] X[k+1] X[j-1] X[j+1] X[i-1] m m+1 k-1 k k+1 j-1 j j+1 i-1 i Supponiamo che X[j], X[k], X[i] e anche che X[j+1] X[j+2] X[i] mentre X[j] > X[j+1] X[k+1] X[k+2] X[j] e X[k] > X[k+1] X[m+1] X[m+2] X[k] e X[m] > X[m+1] m è l indice del primo elemento verso destra tale che X[m] > X[i]. Per definizione sappiamo che S[i] = i - m 27

Analisi del problema: caso X[i+1] X[i] S[i] = s se X[i-s],,X[i-1], X[i] X[i] e X[i-(s+1)]>X[i] X[m] X[k] X[j] X[i+1] X[m+1] X[k-1] X[k+1] X[j-1] X[j+1] X[i-1] X[i] m m+1 k-1 k k+1 j-1 j j+1 i-1 i i+1 S[i+1] = S[i] + S[j] +S[k] perché se X[j+1] X[j+2] X[i], già S[i] tiene conto degli elementi da X[j+1] a X[i], che sono tutti più piccoli di X[j], così come S[k] per gli elementi da X[k+1] a X[j]. Essendo X[m] > X[i+1], è m l indice sul quale fermare la somma 28

Analisi del problema: caso X[i+1]<X[i] S[i] = s se X[i-s],,X[i-1], X[i] X[i] e X[i-(s+1)]>X[i] X[m] X[k] X[j] X[k-1] X[m+1] X[k+1] X[j-1] X[j+1] X[i-1] X[i] X[i+1] m m+1 k-1 k k+1 j-1 j j+1 i-1 i i+1 S[i+1] = 1 perché se X[i+1] < X[i] l unico elemento minore o uguale a X[i+1] è X[i+1] stesso. 29

Passo di calcolo, quando X[i+1] X[i] S[i] = s se X[i-s],,X[i-1], X[i] X[i] e X[i-(s+1)]>X[i] X[m] X[k] X[j] X[i+1] X[k-1] X[m+1] X[k+1] X[j-1] X[j+1] X[i-1] X[i] m m+1 k-1 k k+1 j-1 j j+1 i-1 i i+1 S[i+1]=S[i] + S[j] +S[k] Ci servirebbe conoscere j,k e m per poter confrontare X[i+1] con X[j], X[k] e X[m]. Se impilassimo l indice h quando consideriamo X[h] e lo estraessimo dalla pila se X[h+1] > X[h], nel nostro esempio nella pila dovremmo avere, quando consideriamo i+1 e prima di ogni confronto, gli indici i,j,k,m con i in cima. Se eliminassimo dalla pila gli indici corrispondenti a elementi minori o uguali a X[i+1], rimarremmo con m in cima alla pila e potremmo porre S[i+1] = i+1 - m, semplicemente perché questo è il numero degli elementi compresi tra gli indici m+1 e i+1 (i+1-(m+1) + 1 = i+1-m), che ora sappiamo sono tutti quelli più piccoli, consecutivi e immediatamente precedenti a X[i+1]. 30

Passo di calcolo, quando X[i+1]<X[i] S[i] = s se X[i-s],,X[i-1], X[i] X[i] e X[i-(s+1)]>X[i] X[m] X[k] X[j] X[k-1] X[m+1] X[k+1] X[j-1] X[j+1] X[i-1] X[i] X[i+1] m m+1 k-1 k k+1 j-1 j j+1 i-1 i i+1 Nella pila ci sono i,j,k,m con i in cima, ma X[i+1] < X[i], per cui i non deve essere tolto dalla pila (più avanti potrei incontrare un elemento maggiore). Sappiamo che S[i+1] deve essere 1, e possiamo ottenerlo sempre usando l elemento in cima alla pila perché S[i+1] = i+1 - i = 1 31

L algoritmo in bozza Quando prendiamo in considerazione l elemento di indice i: 1. eliminiamo dalla pila gli indici j tali che X[i] X[j] 2. se la pila è vuota si pone S[i] = i+1 3. altrimenti S[i] = i h, dove h è l indice in cima alla pila, 4. si impila i La correttezza segue da quanto visto prima, infatti esaminiamo i vari casi: se la pila è vuota si danno due casi 1. all inizio e allora S[0] =1, 2. considerando X[i], tutti gli indici impilati sono stati eliminati dalla pila, quindi erano tutti relativi ad elementi minori o uguali a quello che si sta considerando, dunque S[i] = i+1. altrimenti la cima della pila contiene l indice, h, del primo elemento da destra maggiore di X[i] e quindi S[i] = i-h 32

Algoritmo2 per span Algorithm span2(x, n) sia S un array of n interi sia A una pila vuota for i = 0 to n 1 do while (not A.isEmpty() and X[A.top()] X[i] ) do A.pop() if A.isEmpty() then S[i] = i + 1 all inizio S[0] = 1, e se la pila si svuota vuol dire che tutti i precedenti sono minori o uguali a X[i] else A.push(i) return S S[i] = i A.top() X[A.top()] > X[i] N.B. : A.top() è l elemento in cima alla pila A A.pop() elimina dalla pila A l elemento in cima A.push(x) inserisce x come elemento in cima alla pila A A.isEmpty() dà true se A è vuota, false altrimenti 33

É lineare? Algorithm span2(x, n) sia S un array of n interi sia A una pila vuota for i = 0 to n 1 do while (nota.isempty() and X[A.top()] X[i] ) do A.pop() if A.isEmpty() then S[i] = i + 1 else S[i] = i A.top() A.push(i) return S Ogni indice dell array è impilato ed eventualmente tolto dalla pila una volta sola. Quindi il ciclo for esterno è eseguito al più n volte. L algoritmo span2 ha tempo di esecuzione asintotico O(n), e necessita di spazio O(n), per la pila. 34