Strutture Dati Dinamiche. Credits Prof Alessandro Campi

Documenti analoghi
Strutture Dati Dinamiche. Monday, January 10, 2011

Dipartimento di Elettronica, Informazione e Bioingegneria API 2013/4

ADT STACK (PILA) ADT STACK (PILA)

ADT STACK (PILA) Svariate applicazioni del concetto di stack:

Algoritmi e Strutture Dati

Algoritmi e Strutture Dati

Algoritmi e Strutture Dati

ADT STACK (PILA) ADT STACK (PILA) IL COMPONENTE STACK (PILA) CONSIDERAZIONI. Per utilizzare istanze del tipo di dato astratto

ADT STACK (PILA) Svariate applicazioni del concetto di stack:

Strutture dati Alberi binari

L albero e un tipo astratto di dati usato per rappresentare relazioni gerarchiche.

Algoritmi e Strutture di Dati

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

STRUTTURE. Sommario Introduzione Definizione di strutture Inizializzazione di strutture

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

Tipi di dato e Strutture dati elementari

STRUTTURE Prentice Hall, Inc. All rights reserved.

Liste concatenate. Violetta Lonati

Alberi binari e alberi binari di ricerca

Alberi ed Alberi Binari

10/10/2016. Caratteristiche degli array. Il tipo RECORD. Il record. LABORATORIO DI PROGRAMMAZIONE 2 Corso di laurea in matematica

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

Alberi binari e alberi binari di ricerca

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

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

La struttura dati CODA

Alberi binari e alberi binari di ricerca

Prova di Algoritmi e s.d. (1o anno) 7 Febbraio TESTO e RISPOSTE

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

PILE E CODE. Pile (stack):

Esercitazione 11. Liste semplici

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

Laboratorio di Programmazione

tipi di dato astratti

Organigramma Gerarchia. (Tree) Nessuna persona può avere più di un superiore Ogni persona può essere superiore di altre

Prova di Algoritmi e s.d. (1o anno) 17 Settembre TESTO e RISPOSTE

Lezione 12 Tabelle Hash

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

Fondamenti di Informatica e Laboratorio T-AB T-15 Strutture dati

Alberi Binari Alberi Binari

Esercitazione 6. Alberi binari di ricerca

Liste. Costo operazioni Tutte le operazioni hanno costo O(1)

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

PROGRAMMAZIONE II canale A-D luglio 2008 TRACCIA DI SOLUZIONE

Strutture dati concatenate

Informatica 3. Informatica 3. LEZIONE 13: Liste doppie, pile e code. Lezione 13 - Modulo 1. Free list (2) Free list. Free list

Informatica 3. LEZIONE 13: Liste doppie, pile e code. Modulo 1: Free list Modulo 2: Lista doppia Modulo 3: Pila Modulo 4: Coda

Programmazione II canale AD -- Esonero del 21/04/2006

Esercitazioni di Fondamenti di Informatica - Lez. 8 27/11/2018

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

Previously on TDP. LISTA rappresentata mediante "struct e puntatori" Cosa e aux? Di che tipo e e cosa contiene?

Strutture dati. Le liste

Orario delle lezioni LABORATORIO TEORIA. mercoledì. martedì aula P/4. lab Turing. Strutture Dati

d. Cancellazione del valore 5 e. Inserimento del valore 1

TIPO DI DATO ASTRATTO

Pile e code. ADT e strutture dati per la rappresentazione di sequenze ad accesso LIFO e FIFO

La Struttura Dati Pila

Esercitazione 7. Grafi. Rappresentazione e algoritmi di visita

La struttura dati PILA

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

Laboratorio di Programmazione

Strutture dati elementari. Vittorio Maniezzo - Università di Bologna

LABORATORIO DI ALGORITMI E STRUTTURE DATI A-L. Ingegneria e scienze informatiche Cesena A.A: 2016/2017 Docente: Greta Sasso

Fondamenti di Informatica II

Università degli Studi di L Aquila Facoltà di Scienze M.F.N. Corso di Laurea in Informatica. Modulo di Laboratorio di Algoritmi e Strutture Dati

Alberi. CORDA Informatica. A. Ferrari. Testi da. Marco Bernardo Edoardo Bontà. Dispense del Corso di. Algoritmi e Strutture Dati

ALBERI. Un Albero. Un grafo aciclico che non è un albero: due archi entranti in uno stesso nodo

Algoritmi e Strutture Dati Laboratorio 20/10/2008. Prof. Pier Luca Lanzi

Struttura dati Dizionario

Alberi. Alberi: Esempio di utilizzo

Strutture dati dinamiche in C. Emilio Di Giacomo

Allocazione dinamica memoria

Esercizi Strutture dati di tipo astratto

Linguaggio C. Esercizio 1

Liste concatenate e allocazione dinamica

Metodi di una Collection

E12 Esercizi su Strutture dati dinamiche in C

Alberi n-ari: specifiche sintattiche e semantiche. Realizzazioni. Visita di alberi n-ari.

Un esempio di mutua ricorsione

Note per la Lezione 4 Ugo Vaccaro

Alberi. Cosa sono gli alberi? Strutture gerarchiche di ogni tipo. Definizione, realizzazione e algoritmi elementari. Generale.

Alberi. Alberi: definizioni. Alberi Binari. Esercizi su alberi binari: metodi ricorsivi. Struttura dati per alberi generici. ASD-L - Luca Tesei

Linguaggio C: Strutture e Liste Concatenate Valeria Cardellini

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

Informatica 3. Informatica 3. LEZIONE 17: Alberi generici. Lezione 17 - Modulo 1. Introduzione. ADT dell albero generico.

Algoritmi e strutture dati

ALGORITMI E STRUTTURE DATI

Strutture dati elementari

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

Informatica 3. LEZIONE 17: Alberi generici. Modulo 1: Definizione e ADT Modulo 2: Implementazione Modulo 3: Alberi e classi di equivalenza

Strutture dati dinamiche in C (II)

Implementazione dell albero binario in linguaggio C++

Transcript:

Strutture Dati Dinamiche Credits Prof Alessandro Campi 1

Che cos è una struttura dati Per struttura dati si intende comunemente la rappresentazione dei dati e le operazioni consentite su tali dati In linguaggi più evoluti del C esistono librerie di strutture dati la Standard Template Library in C++ le Collection in Java 2

Strutture dati più comuni Lista concatenata (linked list) Pila (stack) Coda (queue) Insieme (set) Multi-insieme (multiset o bag) Mappa (map) Albero (tree) Grafo (graph) 3

Lista Concatenata Una vecchia conoscenza, ormai! typedef struct Nodo { Tipo dato; struct Nodo *next; nodo; typedef nodo * lista; L'abbiamo già trattata come TDA 4

Pila (o stack) È una struttura dati con accesso limitato all elemento "in cima", che è quello inserito più recentemente (LIFO: last in, first out) Nomi standard delle operazioni: Push Inserimento in cima Pop prelievo e rimozione dell elemento in cima, che è per definizione l'ultimo elemento inserito Top o Peek Prelievo senza rimozione (cioè "sola lettura") dell'elemento in cima 5

Applicazioni della pila Controllo di bilanciamento di simboli (parentesi, tag XML, blocchi C, ) Per ogni simbolo di "apertura" si impila un segnaposto che sarà rimosso quando si incontra il simbolo duale di "chiusura" Se il simbolo non corrisponde al segnaposto sulla pila, allora la sequenza non è bilanciata Esempio: {[][{[](){](){[()]()[{] ok {[{] () ko Implementazione di chiamate a funzione Pila di sistema e record di attivazione Valutazione di espressioni aritmetiche 6

Pila (o stack): implementazione PILA (stack): I nuovi nodi possono essere aggiunti e cancellati solo dalla cima (top) della pila La base della pila è indicata da un puntatore a NULL È una versione vincolata della lista concatenata push Aggiunge un nuovo nodo alla cima della pila Passo la testa per riferimento, quindi non restituisce nulla al chiamante (void) pop Cancella un nodo dalla cima della pila Memorizza il valore cancellato Passo la testa per riferimento e quindi restituisce un valore booleano al chiamante true se l operazione ha avuto successo 7

typedef struct snode { int data; struct snode * nextptr; StackNode; typedef StackNode *StackNodePtr; void push( StackNodePtr *, int ); int pop( StackNodePtr * ); int isempty( StackNodePtr ); void printstack( StackNodePtr ); void instructions( void ); 8

int main() { StackNodePtr stackptr = NULL; /* punta alla base (= alla cima!) della pila */ int choice, value; do { instructions(); scanf( "%d", &choice ); switch ( choice ) { case 1: printf( "Enter an integer: " ); /* caso push */ scanf( "%d", &value ); push( &stackptr, value ); printstack( stackptr ); break; case 2: if (!isempty( stackptr ) ) /* caso pop */ printf( "The popped value is %d.\n", pop( &stackptr ) ); printstack( stackptr ); break; case 3: printf( "End of run.\n" ); break; default: printf( "Invalid choice.\n\n" ); break; while ( choice!= 3 ); 9

int main() { StackNodePtr stackptr = NULL; /* punta alla base (= alla cima!) della pila */ int choice, value; do { instructions(); scanf( "%d", &choice ); switch ( choice ) { case 1: printf( "Enter an integer: " ); /* caso push */ scanf( "%d", &value ); push( &stackptr, value ); printstack( stackptr ); break; case 2: if (!isempty( stackptr ) ) /* caso pop */ printf( "The popped value is %d.\n", pop( &stackptr ) ); printstack( stackptr ); break; case 3: printf( "End of run.\n" ); break; default: printf( "Invalid choice.\n\n" ); break; while ( choice!= 3 ); Push e pop modificano la lista, devo passare l indirizzo del puntatore alla lista (i.e., l indirizzo nello stack). Altrimenti restituisco la pila e la sovrascrivo, come visto precedentemente 10

void instructions( void ) { printf( "Enter choice:\n"); printf( "1 to push a value on the stack\n"); printf( "2 to pop a value off the stack\n"); printf( "3 to end program\n\n> "); void push( StackNodePtr *topptr, int info ) { StackNodePtr newptr; newptr = malloc( sizeof( StackNode ) ); if ( newptr!= NULL ) { newptr->data = info; newptr->nextptr = *topptr; *topptr = newptr; printf( "%d not inserted. No memory available.\n", info ); 11

int pop( StackNodePtr *topptr ) { StackNodePtr tempptr = *topptr; int popvalue = (*topptr)->data; *topptr = (*topptr)->nextptr; free( tempptr ); return popvalue; void printstack( StackNodePtr currentptr ) { if ( currentptr == NULL ) printf( "The stack is empty.\n\n" ); { printf( "The stack is:\n" ); while ( currentptr!= NULL ) { printf( "%d --> ", currentptr->data ); currentptr = currentptr->nextptr; printf( "NULL\n\n" ); int isempty( StackNodePtr topptr ) { return topptr == NULL; 12

Coda (o queue) In una coda l accesso è ristretto all elemento inserito meno recentemente (FIFO) Le operazioni tipiche supportate dalla coda sono: enqueue o offer aggiunge un elemento in coda dequeue o poll o remove preleva e cancella l elemento di testa getfront o peek preleva ma non cancella l elemento di testa 13

typedef struct qnode { int data; struct qnode *nextptr; QueueNode; typedef QueueNode *QueueNodePtr; void printqueue( QueueNodePtr ); int isempty( QueueNodePtr ); int dequeue( QueueNodePtr *, QueueNodePtr * ); void enqueue( QueueNodePtr *, QueueNodePtr *, int ); void instructions( void ); 15

int main() { QueueNodePtr firstptr = NULL, lastptr = NULL; int choice, item; do { instructions(); scanf( "%d", &choice ); switch( choice ) { case 1: printf( "Enter an integer: " ); scanf( "\n%d", &item ); enqueue(&firstptr, &lastptr, item ); printqueue( firstptr ); break; case 2: if (!isempty( firstptr ) ) { item = dequeue( &firstptr, &lastptr ); printf( "%d has been dequeued.\n", item ); printqueue( firstptr ); break; case 3: printf( "End of run.\n" ); break; default: printf( "Invalid choice.\n\n" ); break; while ( choice!= 3 ); 16

void instructions( void ) { printf ( "Enter your choice:\n ); printf ( " 1 to add an item to the queue\n" ); printf ( " 2 to remove an item from the queue\n" ); printf ( " 3 to end\n\n> " ); void enqueue( QueueNodePtr *firstptr, QueueNodePtr *lastptr, int value ) { QueueNodePtr newptr; newptr = malloc( sizeof( QueueNode ) ); if ( newptr!= NULL ) { newptr->data = value; newptr->nextptr = NULL; if ( isempty( *firstptr ) ) *firstptr = newptr; ( *lastptr )->nextptr = newptr; *lastptr = newptr; printf( "%c not inserted. No memory available.\n", value); 17

int dequeue( QueueNodePtr *firstptr, QueueNodePtr *lastptr ) { QueueNodePtr tempptr = *firstptr; int value = ( *firstptr )->data; *firstptr = ( *firstptr )->nextptr; if ( *firstptr == NULL ) *lastptr = NULL; free( tempptr ); return value; void printqueue( QueueNodePtr currentptr ) { if ( currentptr == NULL ) printf( "Queue is empty.\n\n" ); { printf( "The queue is:\n" ); while ( currentptr!= NULL ) { printf( "%d --> ", currentptr->data ); currentptr = currentptr->nextptr; printf( "NULL\n\n" ); int isempty( QueueNodePtr firstptr ) { return fearstptr == NULL; 18

Insieme (o set) È come la lista, ma con il vincolo di non ammettere valori duplicati L ordine in cui appaiono gli elementi nella lista è trasparente all utente Necessariamente, poiché non è significativo Di solito, per motivi di convenienza, gli insiemi si realizzano tramite liste ordinate Così si velocizzano le operazioni di ricerca (dicotomica / binary search) e inserimento 19

Prototipi per l insieme int add ( Tipo item, Insieme * i ); aggiunge l elemento dato all insieme e restituisce un valore (booleano) che indica se l operazione ha avuto successo int remove ( Tipo item, Insieme * i ); rimuove l elemento indicato dall insieme e restituisce un valore (booleano) che indica se l operazione ha avuto successo int contains ( Tipo item, Insieme i ); verifica se l elemento dato è presente nell insieme (restituisce un valore booleano) 20

Albero binario Un albero binario è una struttura dati dinamica in cui i nodi sono connessi tramite rami ad altri nodi in modo che: c'è un nodo di partenza (la radice ) ogni nodo (tranne la radice) è collegato ad uno e un solo nodo padre ogni nodo è collegato al massimo a due altri nodi, detti figli (max due binario) i nodi che non hanno figli sono detti foglie 21

Struttura di un albero binario typedef struct El { Tipo dato; struct El *left; struct El *right; Nodo; typedef Nodo * Tree; 22

Struttura di un albero binario typedef struct El { Tipo dato; struct El *left; struct El *right; Nodo; typedef Nodo * Tree; STRUTTURA RICORSIVA Un albero è un nodo da cui "spuntano" due alberi! (l'albero destro e l'albero sinistro) 23

Struttura di un albero binario typedef struct El { int dato; struct El *left; struct El *right; Nodo; typedef Nodo * Tree; 12 4 15 25 2 8 16 5 7 10 0 24

Piccoli esercizi su alberi binari NATURALMENTE in versione ricorsiva! Per capire quanto sia più semplice formulare questi problemi e le relative soluzioni in forma ricorsiva è sufficiente provare a fare diversamente! Esercizi "facili": Conteggio dei nodi Calcolo della profondità Ricerca di un elemento Conteggio dei nodi foglia Conteggio dei nodi non-foglia Conteggio dei nodi su livello pari/dispari Un esercizio difficile: Verifica di "simmetria speculare" dell'albero: OK KO OK KO 25

Conteggio dei nodi int contanodi ( Tree t ) { /* c è anche il nodo corrente */ 26

12 4 15 25 2 8 16 5 7 10 0 27

12 4 15 25 2 8 16 5 7 10 0 28

12 4 15 25 2 8 16 5 7 10 0 29

1 12 4 15 25 2 8 16 5 7 10 0 30

1 12 4 15 25 2 8 16 5 7 10 0 31

1 12 4 15 25 2 8 16 5 7 10 0 32

1 1 12 4 15 25 2 8 16 5 7 10 0 33

1 1 12 4 15 25 2 8 16 5 7 10 0 34

1 12 4 15 25 2 8 16 5 7 10 0 1 35

1 1 1 12 4 15 25 2 8 16 5 7 10 0 36

1 1 2 12 4 15 25 2 8 16 5 7 10 0 37

1 4 12 4 15 25 2 8 16 5 7 10 0 38

6 12 4 15 25 2 8 16 5 7 10 0 39

6 1 12 4 15 25 2 8 16 5 7 10 0 40

6 1 2 12 4 15 25 2 8 16 5 7 10 0 41

6 4 12 4 15 25 2 8 16 5 7 10 0 42

Restituisce 11 12 4 15 25 2 8 16 5 7 10 0 43

Calcolo della profondità int depth ( Tree t ) { int D, S; if (t == NULL) S = depth( t->left ); D = depth( t->right ); if ( S > D ) return S + 1; return D + 1; 44

Calcolo della profondità int max(int a,int b) { if(a>b) return a; return b; int depth ( Tree t ) { if (t == NULL) return max(depth( t->left ),depth( t->right ))+1; 45

int depth ( Tree t, int currentdepth) { int D, S; return currentdepth; { S = depth( t->left, currentdepth+1 ); D = depth( t->right, currentdepth+1 ); Ancora la profondità (variante) if ( S > D ) return S; return D; Questa versione utilizza il concetto "sussidiario" di livello di profondità del nodo corrente, che è incrementato di una unità ad ogni chiamata che scende "più in profondità Va invocato nel main passando 0 come secondo parametron, i.e., d = depth(pt, 0); 46

Ricerca di un elemento in un albero Restituisce NULL se non trova nell albero t il dato d, altrimenti restituisce il puntatore al primo nodo che lo contiene, effettuando la visita di t in preordine sinistro Tree trova ( Tree t, Tipo d) { Tree temp; if ( t == NULL) return NULL if ( t->dato == d ) /* Se Tipo ammette l operatore == */ return t; temp = trova( t->left, d ); if ( temp == NULL ) return trova( t->right, d ); return temp; 47

Ricerca di un elemento in un albero ordinato Restituisce NULL se non trova nell albero t il dato d, altrimenti restituisce il puntatore al primo nodo che lo contiene, effettuando la visita di t in preordine sinistro tree trova ( tree t, Tipo d) { tree temp; if ( t == NULL) return NULL if ( t->dato == d ) /* Se Tipo ammette l operatore == */ return t; if(t->dato > d) return trova( t->left, d ); return trova( t->right, d ); 48

Ricerca di un elemento in un albero Restituisce NULL se non trova nell albero t il dato d, altrimenti restituisce il puntatore al primo nodo che lo contiene, effettuando la visita di t in preordine sinistro tree trova ( tree t, Tipo d) { tree temp,temp2; if ( t == NULL) return NULL if ( t->dato == d ) /* Se Tipo ammette l operatore == */ return t; temp = trova( t->left, d ); temp2 = trova( t->right, d ); if(temp2!=null) retutn temp2; return temp; 49

Numero di nodi foglia int leaves ( Tree t ) { if ( t->left == NULL && t->right == NULL); return 1; return leaves( t->right ) + leaves(t->left); 50

int leaves ( tree t ) { if ( t->left == NULL && t->right == NULL); return 1; return leaves( t->right ) + leaves(t->left); NOOOOOOOOOOOOO, È indispensabile tenere Numero di nodi foglia Darebbe errore nei nodi con un solo figlio, tipo t->left NULL e t->right!= NULL Perché la condizione è falsa e quindi si passa a valutare t->left e qui (bisognerebbe controllare se == NULL come prima cosa), da errore quando prova ad accedere a t->right o t->left 51

int branches ( Tree t ) { if ( t == NULL ( t->left == NULL && t->right == NULL) ) return 1 + branches( t->right ) + branches(t->left); oppure Numero di nodi non-foglia int branches2 ( Tree t ) { return contanodi( t ) leaves( t ); Si noti che la seconda versione scandisce due volte l'intero albero, impiegando circa il doppio del tempo 52

Numero di nodi su livelli dispari int oddcount ( tree t, int level ) { int n; n = oddcount( t->left, level+1 ) + oddcount( t->right, level+1 ); if ( level%2 == 1 ) /* == 0 per la funzione evencount */ n++; return n; Questa formulazione richiede che la prima chiamata abbia la forma: tree t = costruiscialbero( ); int x = oddcount( t, 0 ); /* Considerando pari il livello della radice */ Per la funzione evencount (nodi su livelli pari) si ragiona in modo perfettamente simmetrico, o si fa ancora contanodi oddcount 53

Numero di nodi su livelli dispari int oddcount ( tree t, int level ) { int n; 12 n = oddcount( t->left, level+1 ) + oddcount( t->right, level+1 ); if ( level%2 == 1 ) n++; 4 15 return n; 25 2 8 16 5 7 10 0 54

Numero di nodi su livelli pari Consideriamo che il livello della radice sia pari. Possiamo scrivere una versione di evencount che non necessita di propagare il concetto di livello "in avanti" nelle chiamate ricorsive. int evencount ( tree t ) { int n = 1; /* Il nodo corrente è sempre su livello pari, per ipotesi induttiva*/ if ( t->left!= NULL ) n += evencount( t->left->left ) + evencount( t->left->right ); if ( t->right!= NULL ) n += evencount( t->right->left ) + evencount( t->right->right ); return n; La prima chiamata sarà allora: tree t = costruiscialbero(?filename? ); int x = evencount( t ); 55

Cancellare un albero void cancella(tree *t) { if (*t!=null) { if ((*t)->left!= NULL) cancella(&((*t)->left)); if ((*t)->right!= NULL) cancella(&((*t)->right)); free (*t); *t=null; 56

Componibilità delle strutture dati Alberi di code Liste di liste Alberi di liste di code Il dato contenuto nei nodi della struttura contenente è, in questi casi, una istanza di struttura dinamica del tipo che vogliamo sia il contenuto del resto abbiamo sempre detto che il Tipo del contenuto dei nodi è assolutamente libero 57

Albero n-ario È una generalizzazione dell albero binario ogni nodo ha un numero arbitrario di figli Può anche essere pensato come un grafo Connesso Cioè esiste un cammino di archi tra qualunque nodo e qualunque altro nodo nel grafo Aciclico Tale per cui Ogni nodo (tranne uno, detto radice) ha un solo arco entrante Si usa ad esempio per rappresentare tassonomie e organizzazioni gerarchiche 58

Grafo Un grafo è un insieme di vertici (o nodi) collegati da lati (o archi) Può essere rappresentato, ad esempio, come la coppia dei due insiemi seguenti: un insieme di vertici un insieme di coppie di vertici (gli archi) 59