APPUNTI SUL LINGUAGGIO C Implementazioni di Liste Concatenate Corso di Algoritmi e Strutture Dati APPUNTI SUL LINGUAGGIO C Implementazioni di Liste Concatenate
Le Liste Concatenate Una lista concatenata è una collezione lineare di strutture connesse da puntatori, detti link (anelli o collegamenti), da cui il termine concatenata. Elemento Elemento Elemento
Lista semplicemente concatenata Un elemento x di una lista è un oggetto con almeno due campi info[x] campo chiave next[x] puntatore al successore dell’oggetto nella lista Una lista L è un oggetto con un campo: head[L] puntatore al primo elemento x della lista Lista vuota: head[L] = NIL head[L] NIL
Implementazione tramite array Occorre conoscere la dimensione max della lista Può portare a spreco di memoria A0 A1 A2 AN-3 AN-2 AN-1 Elemento non usato
Complessita' delle operazioni operazione lista come array Inserzione di un elemento in testa O(n) Inserzione di un elemento in coda O(1) Rimozione di un elemento in testa Rimozione di un elemento in coda Concatenazione di liste Visita/Rimozione di un elemento dato qualunque
Implementazione tramite record e puntatori // definizione della struttura nodo typedef struct elem{ int info; // <tipo> <valore del nodo>; struct elem* next; } elist; // definizione del puntatore ai nodi della lista typedef elist* plist;
Le Liste semplicemente Concatenate info plist info
Complessita' delle operazioni operazione lista come array lista semplicemente concatenata (puntatori e record) Inserzione di un elemento in testa O(n) O(1) Inserzione di un elemento in coda Rimozione di un elemento in testa Rimozione di un elemento in coda Concatenazione di liste Visita/Rimozione di un elemento dato qualunque
Stack LIFO La disciplina di inserzione/cancellazione degli elementi e’ LIFO (Last In - First Out): l’ultimo elemento inserito e’ il primo elemento estratto E’ sufficiente: Un riferimento tra gli elementi della lista Un marker per indicare che un riferimento e’ non significativo (e.g. per l’ultimo elemento della lista) Una testata per accedere alla lista (alla sua testa) Le operazioni di inserimento e cancellazione (entrambe in testa!) hanno costo costante (O(1)) el.n el.2 el.1 ••• testata
Coda FIFO : doppio puntatore di testata La disciplina di inserzione/cancellazione degli elementi e’ FIFO (First In - First Out): il primo elemento inserito e’ il primo elemento estratto Inserzione ed estrazione avvengono agli estremi opposti della lista L' operazione di cancellazione in testa ha costo costante (O(1)) (anche quella di inserimento in testa, ma non ci interessa!) L'operazione di inserimento in fondo ha costo costante (O(1)) Con un solo puntatore di testata una delle due operazioni (l’inserimento in fondo) avrebbe un costo O(n) (dovrei scandire tutta la lista) Con due puntatori di testata, uno al primo e l'altro all'ultimo elemento della lista, si riconducono entrambe le operazioni a complessita' costante el.1 el.2 el.n ••• Testata - first - last
Complessita' delle operazioni operazione lista semplicemente concatenata con doppio puntatore lista semplicemente concatenata con singolo puntatore Inserzione di un elemento in testa O(1) Inserzione di un elemento in coda O(n) Rimozione di un elemento in testa Rimozione di un elemento in coda Concatenazione di liste Rimozione di un elemento dato qualunque
Le liste concatenate Tipi di liste concatenate: Lista semplicemente concatenata Vi si accede con un puntatore esterno collegato al primo nodo Termina con un puntatore nullo contenuto nell’ultimo nodo E’ attraversata/scandita in un sola direzione (quella data dai puntatori) Lista circolare semplicemente concatenata Come la precedente, ma in aggiunta il puntatore contenuto nell’ultimo nodo si ricollega indietro al primo nodo della lista Lista doppiamente concatenata Vi si può accedere tramite due distinti puntatori esterni, uno collegato al primo nodo e l’altro all’ultimo nodo Ogni nodo ha due puntatori, uno che va avanti verso il nodo successivo ed uno che ritorna indietro al precedente Il puntatore che va avanti dell’ultimo nodo è nullo così come quello che va indietro del primo nodo Può essere attraversata/scandita sia in avanti che indietro (fino ai suoi estremi) Lista circolare doppiamente concatenata Come la precedente, ma il puntatore che va avanti dell’ultimo nodo si collega al primo nodo e il puntatore che va indietro del primo all’ultimo nodo
Coda FIFO : lista circolare con testata che riferisce l'ultimo elemento della lista Immaginiamo una lista circolare in cui la testata riferisca l'ultimo elemento anziche' il primo: Si puo' inserire in tempo costante un elemento sia in testa che in coda alla lista Si puo' cancellare in tempo costante l'elemento in testa alla lista Si possono liberare tutti i nodi di una lista in tempo costante inserendoli in una lista dei liberi La cancellazione dell'elemento in coda alla lista ha costo O(n) L'eliminazione di un elemento qualunque della lista (possedendone il riferimento) ha tempo O(n) testata el.1 el.2 ••• el.n
Complessita' delle operazioni operazione lista circolare semplicemente concatenata lista semplicemente concatenata con doppio puntatore Inserzione di un elemento in testa O(1) Inserzione di un elemento in coda Rimozione di un elemento in testa Rimozione di un elemento in coda O(n) Concatenazione di liste Rimozione di un elemento dato qualunque
Eliminazione dei casi particolari - 1 Consideriamo di avere una lista ordinata di interi rappresentata come in figura: dove el.1 el.2 … el.n e di volere scrivere una procedura per l'inserzione ordinata di un nuovo elemento: typedef struct elem { int value; struct elem *next; } node; typedef node *sortedIntList; el.1 el.2 el.n ••• testata
Eliminazione dei casi particolari - 2 sortedIntList ordInsert(sortedIntList *l, int newEl) { if (*l == NULL) { // caso 1: lista vuota *l = (sortedIntList)malloc(sizeof(node)); (*l)->value = newEl; (*l)->next = NULL; return (*l); } else if (newEl <= (*l)->value) { // caso 2: inserisco elemento minimo, al primo posto node *t = *l; (*l)->next = t; } else { // caso 3 : inserisco elemento non minimo // in lista non vuota // continua alla prossima pagina
Eliminazione dei casi particolari - 3 sortedIntList s = *l; // individua il nodo precedente il nuovo // elemento while (s->next != NULL && s->next->value < newEl) { s = s->next; } sortedIntList t = (sortedIntList)malloc(sizeof(node)); t->value = newEl; t->next = s->next; s->next = t; return (t);
Eliminazione dei casi particolari - 4 I casi 1 e 2 rappresentano casi particolari, a fronte del caso 3 che e' il caso generale. (N.B.: in realta' la distinzione tra i casi 1 e 2 e' stata costruita ad arte a scopo didattico) Si potrebbero riassorbire i casi particolari in quello generale, semplificando cosi' la procedura? Si', basta inserire un nodo dummy in testa alla lista! Il valore del nodo dummy e' ovviamente irrilevante. Il nodo dummy e' sempre presente nella lista, anche quando questa e' vuota. dummy el.1 el.n ••• testata
Eliminazione dei casi particolari - 5 typedef struct elem { int value; struct elem *next; } node; typedef node *sortedIntList; void listInit(sortedIntList *l) { *l = (sortedIntList)malloc(sizeof(node)); (*l)->value = 0; // un valore dummy (*l)->next = NULL; } int listEmpty(sortedIntList l) { return(l->next == NULL);
Eliminazione dei casi particolari - 6 sortedIntList ordInsert(sortedIntList l, int newEl) { // N.B.: l puo' essere passata per valore perche' // non c'e' mai necessita' di modificare la // testata sortedIntList s = l; // individua il nodo precedente il nuovo elemento while (s->next != NULL && s->next->value < newEl) { s = s->next; } sortedIntList t = (sortedIntList)malloc(sizeof(node)); t->value = newEl; t->next = s->next; s->next = t; return (l);
Lista Doppiamente Concatenata Le liste i cui elementi sono collegati da un unico riferimento (link) hanno diversi problemi. Dato un elemento (il riferimento ad un nodo della lista) la lista puo' essere scandita solo in una direzione non si puo' conoscere l'elemento precedente quello dato (e.g. per rimuovere l'elemento dato) se non scandendo tutta la lista quindi l'eliminazione di un elemento qualunque della lista (pur possedendone il riferimento) ha complessita' lineare (O(n)) Questi problemi sono risolti se dotiamo ogni elemento della lista di due riferimenti, uno in avanti e l'altro all'indietro, cosi' che la lista possa essere scandita in entrambe le direzioni. In questo modo inserzione e rimozione di un elemento in un punto dato della lista (in particolare all'inizio e alla fine) hanno sempre complessita' costante (O(1))
Lista Doppiamente Concatenata Struttura di una lista circolare, doppiamente linkata, con nodo dummy di testata. testata next dummy prev next el.1 prev next el.2 prev next el.n prev
Lista doppiamente concatenata typedef struct elem { int value; struct elem *prev; struct elem *next; } node; typedef node *DoubleLinkedList; void listInit(DoubleLinkedList *l) { *l = (DoubleLinkedList)malloc(sizeof(node)); (*l)->value = 0; // un valore dummy (*l)->prev = NULL; (*l)->next = NULL; } int listEmpty(DoubleLinkedList l) { return(l->next == NULL);
Lista circolare doppiamente concatenata con nodo dummy di testata Esercizio: scrivere in C le procedure per: L'inserimento di un elemento in testa e in coda alla lista La cancellazione di un elemento in testa e in coda alla lista L'eliminazione di un elemento qualunque della lista (passando il valore dell’elemento come parametro formale)
Complessita' delle operazioni operazione lista circolare semplicemente concatenata lista circolare doppiamente concatenata Inserzione di un elemento in testa O(1) Inserzione di un elemento in coda Rimozione di un elemento in testa Rimozione di un elemento in coda O(n) Concatenazione di liste Rimozione di un elemento dato qualunque
Liste e Insiemi Esercizio: Una lista puo' essere utilizzata per rappresentare un insieme. Operazioni tipiche su insiemi sono: intersezione unione inserimento di un elemento in un insieme rimozione di un elemento da un insieme. Definire una rappresentazione tramite lista per insiemi di numeri interi e scrivere delle funzioni C che realizzino queste operazioni. Quale e' la complessita' di queste funzioni? Come cambiano le cose a seconda del fatto che le liste siano mantenute ordinate o meno?
APPUNTI SUL LINGUAGGIO C Implementazioni di Liste Concatenate Corso di Algoritmi e strutture Dati APPUNTI SUL LINGUAGGIO C Implementazioni di Liste Concatenate FINE