Docsity
Docsity

Prepara i tuoi esami
Prepara i tuoi esami

Studia grazie alle numerose risorse presenti su Docsity


Ottieni i punti per scaricare
Ottieni i punti per scaricare

Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium


Guide e consigli
Guide e consigli


Specifiche e implementazioni di ADT Stack e Queue in Java, Appunti di Algoritmi E Strutture Di Dati

Le specifiche sintassiche e semantiche di ADT Stack e Queue, ossia data structures last-in-first-out (LIFO) e first-in-first-out (FIFO) rispettivamente. Vengono presentate le operazioni di push, pop, top, empty, enqueue e dequeue, insieme alle loro specifiche semantiche. Inoltre, vengono discusse le implementazioni di stack e queue utilizzando array e liste collegate.

Tipologia: Appunti

2020/2021

Caricato il 28/03/2022

Acaiazza
Acaiazza 🇮🇹

8 documenti

1 / 11

Toggle sidebar

Questa pagina non è visibile nell’anteprima

Non perderti parti importanti!

bg1
ADT Stack (pila)
Uno stack è una sequenza di elementi di un determinato tipo, in cui è possibile aggiungere o togliere
elementi, uno alla volta, esclusivamente da un unico lato (top dello stack). Questo significa che la sequenza
viene gestita con la modalità detta LIFO (Last-in-first-out) cioè l’ultimo elemento inserito nella sequenza
sarà il primo ad essere eliminato.
Lo stack è una struttura dati lineare, omogenea, a dimensione variabile in cui si può accedere direttamente
solo al primo elemento della lista. Non è possibile accedere ad un elemento diverso dal primo, cioè quello
che è stato inserito per ultimo, se non dopo aver eliminato tutti gli elementi che lo precedono (cioè che
sono stati inseriti dopo).
Gli elementi di un ADT lista possono essere analizzati uno per uno, in modo sequenziale, mentre in uno
stack ciò non si può fare.
Specifica sintattica e semantica
- Specifica sintattica: il tipo di riferimento è il tipo stack mentre i tipi usati sono item e boolean.
Operatori
newStack() → stack
emptyStack(stack) → boolean
push(elem, stack) → stack
pop(stack) → stack
top(stack) → item
- Specifica semantica: stack è l’insieme delle sequenze 𝑆 = 𝑎1, 𝑎2, , 𝑎𝑛 di tipo item. L’insieme stack
contiene inoltre un elemento nil che rappresenta la pila vuota (priva di elementi).
Operatori
newStack() → s
Post: s = nil
emptyStack(s) → b
Post: se s=nil allora b = true altrimenti b = false
push(e, s) → s’
Post: s = <a1, a2, … an> AND s’ = <e, a1, a2, …, an>
pop(s) → s’
Pre: s = <a1, a2, …, an> n>0
Post: s’ = <a2, …, an>
top(s) → e
Pre: s = <a1, a2, …, an> n>0
Post: e = a1
Implementazione con array:
Tra le possibili implementazioni, le più usate sono realizzate tramite array e lista concatenata. Come si può
notare nel caso dell’ADT stack è efficiente anche un’implementazione con array, contrariamente a quanto
visto con le liste, questo perché in questo caso non sono necessari shift tra elementi per inserirne o
rimuoverne altri ma bisogna lavorare solo sul primo elemento.
pf3
pf4
pf5
pf8
pf9
pfa

Anteprima parziale del testo

Scarica Specifiche e implementazioni di ADT Stack e Queue in Java e più Appunti in PDF di Algoritmi E Strutture Di Dati solo su Docsity!

ADT Stack (pila)

Uno stack è una sequenza di elementi di un determinato tipo, in cui è possibile aggiungere o togliere elementi, uno alla volta, esclusivamente da un unico lato (top dello stack). Questo significa che la sequenza viene gestita con la modalità detta LIFO (Last-in-first-out) cioè l’ultimo elemento inserito nella sequenza sarà il primo ad essere eliminato. Lo stack è una struttura dati lineare, omogenea, a dimensione variabile in cui si può accedere direttamente solo al primo elemento della lista. Non è possibile accedere ad un elemento diverso dal primo, cioè quello che è stato inserito per ultimo, se non dopo aver eliminato tutti gli elementi che lo precedono (cioè che sono stati inseriti dopo). Gli elementi di un ADT lista possono essere analizzati uno per uno, in modo sequenziale, mentre in uno stack ciò non si può fare.  Specifica sintattica e semantica

  • Specifica sintattica: il tipo di riferimento è il tipo stack mentre i tipi usati sono item e boolean. Operatori  newStack() → stack  emptyStack(stack) → boolean  push(elem, stack) → stack  pop(stack) → stack  top(stack) → item
  • Specifica semantica: stack è l’insieme delle sequenze 𝑆 = 𝑎 1 , 𝑎 2 , … , 𝑎𝑛 di tipo item. L’insieme stack contiene inoltre un elemento nil che rappresenta la pila vuota (priva di elementi). Operatori  newStack() → s Post: s = nil  emptyStack(s) → b Post: se s=nil allora b = true altrimenti b = false  push(e, s) → s’ Post: s = <a1, a2, … an> AND s’ = <e, a1, a2, …, an>  pop(s) → s’ Pre: s = <a1, a2, …, an> n> Post: s’ = <a2, …, an>  top(s) → e Pre: s = <a1, a2, …, an> n> Post: e = a  Implementazione con array: Tra le possibili implementazioni, le più usate sono realizzate tramite array e lista concatenata. Come si può notare nel caso dell’ADT stack è efficiente anche un’implementazione con array, contrariamente a quanto visto con le liste, questo perché in questo caso non sono necessari shift tra elementi per inserirne o rimuoverne altri ma bisogna lavorare solo sul primo elemento.

Nel caso della realizzazione tramite array lo stack è implementato come un puntatore ad una 𝑠𝑡𝑟𝑢𝑐𝑡 𝑐_𝑠𝑡𝑎𝑐𝑘 che contiene due elementi: un array di MAXSTACK elementi e un intero che indica la posizione del top dello stack. L’intero top inoltre è utile anche per tenere conto della grandezza dello stack. Quando lo stack si riempie, non è possibile eseguire l’operazione push. L’ADT stack è realizzato in modo da non dipendere dal tipo degli elementi contenuti; utilizza il tipo generico item già visto in precedenza; pop e push restituiscono un intero che indica l’esito dell’operazione.

 Implementazione dello stack con liste collegate: Il tipo stack è definito come un puntatore ad una struct che contiene: un intero numelem che indica il numero di elementi dello stack e un puntatore top ad una struct nodo (come per la lista).

 Implementazione dello stack basata sull’uso del modulo lista: Il tipo stack è definito come un puntatore ad una struct che contiene un elemento top di tipo list. Non serve più nemmeno l’intero numelem che indica il numero di elementi dello stack. Anche se abbiamo un solo elemento nella struct, continuiamo a definire il tipo stack come puntatore a struct c_stack per non cambiare la definizione nell’header file.  Implementazione di singola istanza di Stack con array: E se volessimo realizzare una sola istanza di stack? In questo caso non abbiamo bisogno di definire ed esportare un tipo. La struttura dati dello stack la manteniamo in variabili globali accessibili solo all’interno del modulo (dichiarazioni static). Gli operatori della lista non usano parametri di tipo stack, ma operano sulle variabili globali. In questo caso non c’è la definizione del tipo stack nell’header file, gli operatori non hanno parametri di tipo stack e gli operatori newStack, pop e push restituiscono un intero che indica il buon esito dell’operazione.

Moduli e Astrazioni sui dati

Il tipo di dato astratto presenta un modulo che incapsula la definizione del tipo e gli operatori ed esporta il nome del tipo e la signature degli operatori. Il tipo di riferimento compare tra i parametri degli operatori ed è consentito definire variabili di tale tipo (istanziazione) e utilizzarle (referenziazione) come parametri degli operatori. Un oggetto (astratto), invece, ha un modulo che incapsula una struttura dati (istanza) e gli operatori ed esporta la signature degli operatori. Non è consentito referenziare la struttura dati fuori dal modulo e l’uso e la manipolazione dell’oggetto sono consentiti unicamente attraverso i suoi operatori. Un oggetto ha uno stato che può cambiare in seguito all’applicazione di determinate operazioni. Un modulo generico è un template dal quale è possibile istanziare più moduli. È tipicamente parametrico rispetto a un tipo base o al numero di componenti di tipo base (parametri di struttura) Esempio: l’ADT (o oggetto) stack generico Un modulo cliente dovrebbe prima istanziare il modulo specificando i parametri di struttura. In C non abbiamo costrutti per fare questo: dobbiamo definire un tipo generico item e delle costanti, che possiamo cambiare all’occorrenza.

ADT Queue (coda)

Una coda (spesso chiamata anche queue) è una sequenza di elementi di un determinato tipo, in cui gli elementi si aggiungono da un lato (tail) e si tolgono dall’altro lato (head). Questo significa che la sequenza viene gestita con la modalità detta FIFO (First-in-first-out) cioè il primo elemento inserito nella sequenza sarà il primo ad essere eliminato. La coda è una struttura dati lineare a dimensione variabile in cui si può accedere direttamente solo alla testa (head) della lista. Non è possibile accedere ad un elemento diverso da head, se non dopo aver eliminato tutti gli elementi che lo precedono (cioè quelli inseriti prima).  Specifica sintattica: Tipo di riferimento: queue Tipi usati: item, boolean Operatori

  • newQueue() → queue
  • emptyQueue(queue) → boolean
  • enqueue(item, queue) → queue
  • dequeue(queue) → item  Specifica semantica: il tipo di riferimento queue è l’insieme delle sequenze S=a1,a2,…,an di tipo item. L’insieme queue contiene inoltre un elemento nil che rappresenta la coda vuota (priva di elementi).

Operatori

  • newQueue() → q Post: q = nil
  • emptyQueue(q) → b Post: se q=nil allora b = true altrimenti b = false
  • enqueue(e, q) → q’ Post: se q = nil allora q’ = altrimenti se q = <a1, a2, … an> con n > 0 allora q’ = <a1, a2, … an, e >
  • dequeue(q) → a Pre: q = <a1, a2, …, an-1, an> n>0 (q ≠ nil) Post: a = a1 e l’elemento a1 viene rimosso da q  Implementazione della queue con liste collegate: Tra le possibili implementazioni, le più usate sono realizzate tramite lista concatenata o array. A differenza dello stack, per gestire la politica FIFO conviene avere accesso sia al primo elemento (estrazione) sia all’ultimo (inserimento). Il tipo queue è definito come un puntatore ad una struct che contiene:
  • Un intero numelem che indica il numero di elementi della coda;
  • Un puntatore head ad uno struct nodo;
  • Un puntatore tail ad uno struct nodo. L’ADT queue è realizzato in modo da non dipendere dal tipo degli elementi contenuti; utilizza il tipo generico item già visto in precedenza: la funzione dequeue toglie e restituisce l’elemento in testa alla coda e la funzione enqueue restituisce un intero che indica l’esito dell’operazione.

 Implementazione semplice di queue con array: La coda è implementata come un puntatore ad una struct c_queue che contiene due elementi:

  • Un array di MAXQUEUE elementi;
  • Un intero che indica la posizione head della coda;
  • Un intero che indica la posizione tail della coda. Quando la coda si riempie, non è possibile eseguire l’operazione enqueue. Una prima soluzione potrebbe essere: ad ogni rimozione si compatta l’array nelle posizioni iniziali, con uno shift degli elementi; ma questo è molto costoso. Una possibile seconda soluzione è: gestire l’array in modo circolare. In ogni istante, gli elementi della coda si trovano nel segmento head, head+1,... tail- 1 ma non necessariamente head <= tail. Infatti, dopo aver inserito in posizione N-1 (ultima posizione dell’array), se c’è ancora spazio in coda, si inseriscono ulteriori elementi a partire dalla posizione 0 (prima posizione dell’array). In questo modo si riesce a garantire che ad ogni istante la coda abbia capacità massima di N-1 elementi. L’header file non cambia rispetto alla versione con le liste collegate e gli operatori hanno la stessa interfaccia.

 Considerazioni: Abbiamo visto due implementazioni diverse dell’ADT coda.

  1. Lista: pro: è una implementazione espandibile (unico limite è la capacità della memoria); contro: la struttura è più complessa.
  2. Vettore circolare: pro: gli elementi sono memorizzati in modo contiguo e la struttura è più semplice; contro: dimensione fissata, bisogna conoscere a priori il numero massimo di elementi che la coda deve contenere, parte dello spazio è inutilizzato. Note su vettore circolare: potrei usare la realloc per ridimensionare la coda (come fatto per lo stack) e poter quindi sempre inserire elementi? Sì, ma devo considerare che l’ordine degli elementi della coda nell’array non necessariamente va dalla posizione 0 alla posizione n-1.