CODE
Struttura dati astratta Coda La coda o queue è una struttura dati astratta in cui l'inserimento e l estrazione sono consentite solo in specifiche posizioni; in particolare, l'inserimento è consentito solo ad un estremo, detto rear (ultimo), e l estrazione è consentita solo all'altro estremo, detto front (primo). Questo tipo di struttura dati è molto utilizzata in informatica, ad esempio nella gestione delle operazioni da eseguire da parte di un sistema operativo, ed è fondamentale nelle telecomunicazioni, in particolare nelle reti a commutazione di pacchetto, dove descrive la gestione dei pacchetti in attesa di essere trasmessi su un collegamento. Questa gestione sfrutta una politica di accesso ai dati di tipo FIFO (First In First Out), in quanto il primo elemento inserito è anche il primo ad essere elaborato (estratto). Sia data una coda composta dagli elementi a 1, a 2, a 3,., a n e si supponga che la posizione rear sia rappresentata dalla posizione dell elemento a n. L'inserimento di un elemento x produce la nuova coda a 1, a 2, a 3,., a n, x. L elemento di posizione rear diviene x. L estrazione di un elemento della coda si riferisce all elemento di posizione front ossia a 1 ; dopo l operazione di estrazione la coda diviene a 2, a 3,., a n, x.
ADT generici: la Coda Tipo Coda Dati: una sequenza S di n elementi Operazioni: isempty() -> result restituisce true se S è vuota, false altrimenti EnQueue(elem e) aggiunge e come ultimo elemento di S DeQueue() -> elem toglie da S il primo elemento e lo restituisce first() -> elem restituisce il primo elemento di S (senza modificare S)
Esempio Questa sequenza mostra il risultato di una serie di operazioni indicate nella colonna di sinistra (dall alto verso il basso), dove una lettera indica un operazione di inserimento in coda (EnQueue) e un asterisco indica un operazione di estrazione dalla coda (DeQueue). Ogni riga mostra l operazione, la lettera restituita dalla DeQueue e il contenuto della coda ordinato dall elemento inserito meno di recente a quello inserito più di recente, da sinistra a destra
Code Come già detto per gli altri ADT, la realizzazione indicizzata prevede che la sequenzialità degli elementi della pila venga rappresentata dalla adiacenza delle locazioni di memoria utilizzate per contenere ciascun elemento. Nella rappresentazione collegata, la sequenzialità degli elementi è mantenuta tramite i puntatori che collegano le varie strutture informative. I pro e i contro sono quelli già studiati.
IMPLEMENTAZIONE CHE SFRUTTA LA LIBRERIA SULLE LISTE (STRUTTURE COLLEGATE)
Rappresentazione collegata tramite puntatori front.... rear front.... nuovo elemento inserimento rear front.... cancellazione rear
Code /* prima di includere questo file si deve dichiarare il tipo degli elementi da inserire nella coda, mediante una dichiarazione typedef... TipoElemCoda; */ /* definizione del tipo coda */ struct StructCoda { TipoLista primo, ultimo; }; typedef struct StructCoda TipoCoda; /* inclusione del file contenente la dichiarazione del tipo TipoLista e delle operazioni primitive sulle liste */ #include "liste.h"
Code /* implementazione delle operazioni primitive sulle code */ void InitCoda(TipoCoda *c) /* inizializza la coda c ponendo a NULL i puntatori al primo e all'ultimo elemento della coda */ { InitLista(&c->primo); InitLista(&c->ultimo); } /* InitCoda */ bool TestCodaVuota(TipoCoda c) /* restituisce TRUE se la coda c è vuota, FALSE altrimenti */ { return (TestListaVuota(c.primo)); } /* TestCodaVuota */
Code /* implementazione delle operazioni primitive sulle code */ void InizioCoda(TipoCoda c, TipoElemCoda *v) /* restituisce in v il primo elemento della coda c senza modificare c */ { TestaLista(c.primo, v); } /* InizioCoda */
Code /* implementazione delle operazioni primitive sulle code */ void InCoda(TipoCoda *c, TipoElemCoda v) /* inserisce l'elemento v all'ultimo posto della coda c */ { if (TestCodaVuota(*c)) { /* c e' vuota: l'elemento v sarà sia il primo che l'ultimo elemento della coda */ c->primo = malloc(sizeof(tiponodolista)); c->ultimo = c->primo; } else { c->ultimo->next = malloc(sizeof(tiponodolista)); c->ultimo = c->ultimo->next; } c->ultimo->info = v; c->ultimo->next = NULL; } /* InCoda */ Se avessi avuto a disposizione fra le funzioni delle liste una funzione per l inserimento di un elemento in fondo alla lista, l avrei potuta utilizzare; la sua complessità sarebbe però stata dell ordine di n, mentre questa ha complessità costante pari a 1.
Code /* implementazione delle operazioni primitive sulle code */ void OutCoda(TipoCoda *c, TipoElemCoda *v) /* elimina il primo nodo della coda c, restituendone il valore in v */ { InizioCoda(*c, v); /* copia il valore del primo elemento di c in v (se esiste) */ CancellaPrimoLista(&c->primo); /* elimina il primo element di c (se esiste) */ /* se l'elemento eliminato era l'unico elemento presente nella coda, allora si pone a NULL anche il puntatore all'ultimo elemento */ if (c->primo == NULL) c->ultimo = NULL; } /* OutCoda */
IMPLEMENTAZIONE INDICIZZATA
Code Possibili implementazioni concrete indicizzate: Tengo un solo indice della coda e la testa sempre in posizione 0 (implementazione sequenziale) Tengo due indici, (implementazione sequenziale) si incrementano sempre (spreco memoria) e prima o poi esaurisco lo spazio disponibile; se l applicazione lo permette, posso regolarmente riposizionare i due indici a 1 Tengo due indici: implementazione circolare. In momenti differenti la coda occupa posizioni differenti dell anello; problema: dimensione massima dell anello (overflow)
Code: rappresentazione sequenziale La rappresentazione sequenziale di un ADT Coda con vettore può essere realizzata utilizzando due variabili, primo e ultimo, che indicano rispettivamente la posizione del primo e dell'ultimo elemento della coda. Inizialmente, quando la coda è vuota si assume che primo=ultimo=-1. front rear 0 1 2 23 3 56 65 N-1 rear front 0 1 2 45 57 2 87 Prima dell'inserimento, ultimo corrisponde all'ultimo elemento del vettore. Il nuovo elemento può essere inserito nella prima posizione libera del vettore, ottenuta incrementando rear nella seguente maniera: rear N-1 90 13 rear=(rear+1) % N, //N = dimensione vettore
Code: rappresentazione sequenziale L'inserimento di un elemento è possibile solo se la coda non è piena. Il valore di ultimo non può essere più incrementato per inserire un nuovo elemento. La condizione di coda piena è: rear front 0 1 2 N-1 34 65 11 12 23 45 76 front rear 0 1 2 N-1 34 65 11 12 23 45 76 primo = = (ultimo+1)%n coda piena Per quanto riguarda la cancellazione dell'elemento di posizione primo, essa viene realizzata incrementando la variabile primo. Se primo e ultimo sono coincidenti, significa che la coda possiede un solo elemento, cioè quello che deve essere eliminato. Ciò significa che dopo la cancellazione la coda rimarrà vuota.
Code: rappresentazione sequenziale L incremento della variabile primo deve avvenire in modo circolare, analogamente a quanto visto per la variabile ultimo: front rear 0 1 2 45 90 57 front N-1 13 primo=(primo+1) % N, dove N è la dimensione del vettore
Code: rappresentazione sequenziale 2 front 4 rear 9 5 3 8 1 front 3 5 9 rear 2 4 1 8
Code sequenziali /* prima di includere questo file si deve dichiarare: (i) la lunghezza massima della coda; (ii) il tipo degli elementi da inserire nella coda. Questo viene realizzato mediante le seguenti dichiarazioni: #define MaxCoda... typedef... TipoElemCoda; coda primo ultimo */ typedef int TipoPosCoda; struct StructCoda { TipoElemCoda coda[maxcoda]; TipoPosCoda primo, ultimo; }; typedef struct StructCoda TipoCoda; 0 1 2 3 4 5 6 7 8 9 7 2 3 4 9 0 4
Code sequenziali /* implementazione delle operazioni primitive sulle code */ void InitCoda(TipoCoda *c) /* inizializza la coda c ponendo a -1 i puntatori al primo e all'ultimo elemento della coda */ { c->primo = -1; c->ultimo = -1; } /* InitCoda */ bool TestCodaVuota(TipoCoda c) /* restituisce TRUE se la coda c è vuota, FALSE altrimenti */ { return (c.primo == -1); } /* TestCodaVuota */
Code sequenziali /* implementazione delle operazioni primitive sulle code */ void InizioCoda(TipoCoda c, TipoElemCoda *v) /* restituisce in v il primo elemento della coda c senza modificare c */ { if (TestCodaVuota(c)) printf("errore: CODA VUOTA\n"); else *v = c.coda[c.primo]; } /* InizioCoda */ bool TestCodaPiena(TipoCoda c) /* restituisce TRUE se la coda c e' piena, FALSE altrimenti */ { if ((c.primo - c.ultimo == 1) ((c.ultimo - c.primo) == (MaxCoda-1))) return TRUE; else return FALSE; } /* TestCodaPiena */
Code sequenziali /* implementazione delle operazioni primitive sulle code */ void InCoda(TipoCoda *c, TipoElemCoda v) /* inserisce l'elemento v all'ultimo posto della coda c */ { if (TestCodaPiena(*c)) printf("errore: CODA PIENA\n"); else { /* posizionamento indice ultimo alla successiva posizione libera */ if (c->primo == -1) { /* c vuota: l'elemento da inserire sarà sia il primo che l'ultimo elemento di c */ c->ultimo = 0; c->primo = 0; } else /* c non vuota: cambia solo il puntatore all'ultimo elemento */ c->ultimo = (c->ultimo + 1) % MaxCoda; /* assegnazione di v all'ultimo elemento della coda */ c->coda[c->ultimo] = v; } } /* InCoda */
Code sequenziali /* implementazione delle operazioni primitive sulle code */ void OutCoda(TipoCoda *c, TipoElemCoda *v) /* elimina il primo record della coda c, restituendone il valore in v */ { if (TestCodaVuota(*c)) printf("errore: CODA VUOTA\n"); else { *v = c->coda[c->primo]; /* se l'elemento eliminato era l'unico elemento presente nella coda, allora si pone a -1 anche il puntatore all'ultimo elemento */ if (c->primo == c->ultimo) { c->ultimo = -1; c->primo = -1; } else /* c non vuota: cambia solo il puntatore al primo elemento */ c->primo = (c->primo + 1) % MaxCoda; } } /* OutCoda */
Code sequenziali Esercizio: Simulare l attività di un aeroporto con 1 sola pista. In ogni momento o si fa decollare o si fa atterrare un aereo. Gli aerei pronti al decollo o all atterraggio arrivano in momenti casuali: Pista vuota Un aereo sta atterrando o partendo Ci possono essere altri aerei in entrambe le code Diamo precedenza agli atterraggi, cioè un aereo decolla solo se la coda di atterraggio risulta vuota