P - ADT Flashcards
Perché un dato è detto astratto?
Perché maschera la sua implementazione mediante la coppia di moduli interfaccia/implementazione
Quali sono le caratteristiche di un quasi ADT?
- il nuovo tipo viene definito con la typedef nel file.h
- il client include il file.h quindi vede i dettagli interni del dato
- non richiede allocazione dinamica
- il quasi ADT per composti viola la regola 1 mentre quello per le collezioni viola la 2
Quali sono le caratteristiche di un ADT di I classe e le regole? Fare un esempio di file .h e .c con un dato per numeri complessi.
- il nuovo tipo viene dichiarato nel file.h ma lasciato incompleto; viene completamente definito nel file.c
- di solito ciò che viene messo nel file.h è un puntatore a struct incompleta
- il client può usare solo il puntatore a struct
- il puntatore è opaco e si dice handle
Regole:
1) nasconde al client i dettagli
2) permette al client di creare più variabili del tipo ADT
Esempio
complex.h
typedef struct complex_s *Complex;
void funzione(Complex c);
complex.c
struct complex_s { float Re; float Im; };
Quali sono i modi per definire l’ADT Item?
1) typedef int Item;
typedef int Key;
2) typedef char *Item;
typedef char *Key;
3)
typedef struct {
char name[MACX];
int num;
} Item;
typedef char *Key
4)
typedef struct {
char name*;
int num;
} Item;
typedef char *Key
Qual è il prototipo della KEYget in base al tipo di Item per i quasi ADT?
Key KEYget(Item val); nei casi 1 e 2
Key KEYget(Item *pval); nei casi 3 e 4
Quasi ADT, scrivere per il tipo di Item 1 le funzioni:
- Key KEYget(Item val);
- int KEYcompare(Key k1, Key k2);
- Item ITEMscan();
- void ITEMshow(Item v);
- int ITEMless(Item val1, Item val2);
Key KEYget(Item val){
return val;
}
int KEYcompare(Key k1, Key k2){
return k1-k2;
}
Item ITEMscan(){
Item i;
scanf(“%d”, &i);
return I;
}
void ITEMshow(Item v){
printf(“%d”, v);
}
int ITEMless(Item val1, Item val2){
return(KEYget(val1) < KEYget(val2));
}
Quasi ADT, scrivere per il tipo di Item 2 le funzioni:
- Key KEYget(Item val);
- int KEYcompare(Key k1, Key k2);
- Item ITEMscan();
- void ITEMshow(Item v);
- int ITEMless(Item val1, Item val2);
static char buf[MAXC];
Key KEYget(Item val){
return val;
}
int KEYcompare(Key k1, Key k2){
return (strcmp(k1, k2));
}
Item ITEMscan(){
scanf(“%s”, buf);
return strdup(buf);
}
void ITEMshow(Item v){
printf(“%s”, v);
}
int ITEMless(Item val1, Item val2){
return (strcmp(KEYget(val1),KEYget(val2))<0);
}
Quasi ADT, scrivere per il tipo di Item 3 le funzioni:
- Key KEYget(Item *pval);
- int KEYcompare(Key k1, Key k2);
- Item ITEMscan();
- void ITEMshow(Item v);
- int ITEMless(Item val1, Item val2);
static char buf[MAXC];
Key KEYget(Item *pval){
return pval->name;
}
int KEYcompare(Key k1, Key k2){
return (strcmp(k1, k2));
}
Item ITEMscan(){
Item val;
scanf(“%s%d”, val.name, &val.num);
return val;
}
void ITEMshow(Item v){
printf(“%s %d”, v.name, v.num);
}
int ITEMless(Item val1, Item val2){
return (strcmp(KEYget(&val1),KEYget(&val2))<0);
}
Quasi ADT, scrivere per il tipo di Item 4 le funzioni:
- Key KEYget(Item *pval);
- int KEYcompare(Key k1, Key k2);
- Item ITEMscan();
- void ITEMshow(Item v);
- int ITEMless(Item val1, Item val2);
static char buf[MAXC];
Key KEYget(Item *pval){
return pval->name;
}
int KEYcompare(Key k1, Key k2){
return (strcmp(k1, k2));
}
Item ITEMscan(){
Item val;
scanf(“%s%d”, buf, &(val.num));
val.name = strdup(buf);
return val;
}
void ITEMshow(Item v){
printf(“%s %d”, v.name, v.num);
}
int ITEMless(Item val1, Item val2){
return (strcmp(KEYget(&val1),KEYget(&val2))<0);
}
In quali casi di Item ha senso la deallocazione?
Solo nei casi 2 e 4.
Quali tipi di Item usiamo per gli ADT di I classe?
Cosa si trova nel file.h?
Usiamo il 3 e il 4.
file.h
typedef struct item *Item;
typedef struct char *Key;
…
Scrivere le seguenti funzioni per gli ADT di prima classe:
- Key KEYget(Item val);
- int KEYcompare(Key k1, Key k2);
- void ITEMshow(Item val);
- int ITEMless(Item val1, Item val2);
Key KEYget(Item val){
return val->name;
}
int KEYcompare(Key k1, Key k2){
return strcmp(k1, k2);
}
void ITEMshow(Item val){
printf(“%s %d”, val->name, val->num);
}
int ITEMless(Item val1, Item val2){
return strcmp(KEYget(val1), KEYget(val2));
}
Scrivere le funzioni di allocazione, deallocazione e scan di Item per ADT di I classe (3 e 4)
- Item ITEMnew(void);
- void ITEMfree(Item val);
- Item ITEMscan() (3) e void ITEMscan(Item val) (4)
Item ITEMnew(void){
Item val;
val = malloc(sizeof(struct item));
if (val == NULL) return ITEMsetvoid();
val->name = ‘\0’; // 4) val->name = NULL;
val->num = 0;
return val;
}
void ITEMfree(Item val){
free(val->name); //solo nel 4)
free(val);
}
Item ITEMscan(){
Item val = ITEMnew();
scanf(“%s %d”, val->name, &val->num);
return val;
}
void ITEMscan(Item val){
scanf(“%s %d”, buf, &val->num);
val->name = strdup(buf);
}
Quali sono i criteri possibili di Delete nelle code in generale?
- FIFO
- LIFO
- priorità
- casuale
- contenuto (tabella di simboli)
In che modo si possono implementare le code, in generale, e quali sono vantaggi e svantaggi?
Si possono implementare con vettori o liste, in versione quasi ADT o ADT di I classe.
I vettori hanno uno spazio sempre alloccato al massimo previsto quindi sono vantaggiosi per code piene o quasi; per le liste lo spazio è proporzionale al numero effettivo di elementi.
Il quasi ADT prevede delle variabili globali e invisibili ad altri (NON ADT), l’ADT di I classe prevede una struct puntata da handle; la struct ha come campi le variabili globali del quasi ADT
ADT Stack: quali funzioni lo caratterizzano e qual è la loro complessità?
stackpush e stackpop, O(1)
ADT Stack: implementazione con vettore quasi ADT. Scrivere Stack.h e Stack.c con strutture dati e funzioni di inizializzazione, push e pop.
- void STACKinit(int MAXN);
- int STACKempty();
- void STACKpush(Item val);
- Item STACKpop();
stack.h
void STACKinit(int MAXN);
int STACKempty();
void STACKpush(Item val);
Item STACKpop();
stack.c
static Item *s; //vettore di Item
static int N;
void STACKinit(int MAXN){
s = malloc(MAXN*sizeof(Item));
N = 0;
}
int STACKempty() { return N == 0; }
void STACKpush(Item val){
s[N++] = val;
}
Item STACKpop(){
return s[–N];
}
ADT Stack: implementazione con vettore ADT di I classe. Scrivere Stack.h e Stack.c con strutture dati e funzioni di inizializzazione, push e pop.
- STACK STACKinit(int MAXN);
- int STACKempty(STACK sp);
- void STACKpush(STACK sp, Item val);
- Item STACKpop(STACK sp);
stack.h
typedef struct stack *STACK;
STACK STACKinit(int MAXN);
int STACKempty(STACK s);
void STACKpush(STACK s, Item val);
Item STACKpop(STACK s);
stack.c
struct stack { Item *s; int N; };
STACK STACKinit(int MAXN){
STACK sp;
sp = malloc(sizeof(sp));
sp->s = malloc(MAXNsizeof(Item));
sp->N = 0;
return sp;
}
int STACKempty(STACK sp){
return sp->N == 0;
}
void STACKpush(STACK sp, Item val){
sp->s[sp->N++] = val;
}
Item STACKpop(STACK sp){
return sp->s[–(sp->N)];
}
ADT Stack: implementazione con lista, quasi ADT. Scrivere Stack.h e Stack.c con strutture dati e funzioni di inizializzazione, push e pop.
- void STACKinit(int MAXN);
- int STACKempty();
- void STACKpush(Item val);
- Item STACKpop();
stack.h
void STACKinit(int MAXN);
int STACKempty();
void STACKpush(Item val);
Item STACKpop();
stack.c
typedef struct STACKnode *link;
struct STACKnode { Item val; link next; };
static link head;
static link NEW (Item val, link next){
link x;
x = malloc(sizeof(*x));
x->val = val; x->next = next;
return x;
}
void STACKinit(int maxN) { head = NULL; }
int STACKempty() { return head == NULL; }
void STACKpush(Item val) {
head = NEW(val, head);
}
Item STACKpop() {
Item tmp;
tmp = head->val;
link t = head->next;
free(head);
head = t;
return tmp;
}
ADT Stack: implementazione con lista, ADT di I classe. Scrivere Stack.h e Stack.c con strutture dati e funzioni di inizializzazione, push e pop.
- STACK STACKinit(int MAXN);
- int STACKempty(STACK s);
- void STACKpush(STACK s, Item val);
- Item STACKpop(STACK s);
stack.h
typedef struct stack *STACK;
STACK STACKinit(int MAXN);
int STACKempty(STACK s);
void STACKpush(STACK s, Item val);
Item STACKpop(STACK s);
stack.c
typedef struct STACKnode *link;
struct STACKnode { Item val; link next; };
struct stack { link head; };
static link NEW(Item val, link next){
link x;
x = malloc(sizeof(*x));
x->val = val; x->next = next;
return x;
}
STACK STACKinit(int MAXN){
STACK s = malloc(sizeof(*s));
s->head = NULL;
return s;
}
int STACKempty(STACK s){
return s->head == NULL;
}
void STACKpush(STACK S, Item val){
s->head = NEW(val, s->head);
}
Item STACKpop(STACK s){
Item tmp;
tmp = s->head->val;
link t = s->head->next;
free(s->head);
s->head = t;
return tmp;
}
ADT QUEUE: quali operazioni lo caratterizzano e che complessità hanno?
QUEUEput e QUEUEget, O(1)
FIFO
ADT QUEUE: come si può implementare? Quali sono le caratteristiche?
Attraverso lista e vettore.
Il vettore può essere sia lineare che circolare.
- vettore lineare: O(n), head = 0 è fisso e quindi bisogna “scalare” gli elementi
- vettore circolare: O(1), head e tail sono incrementati modulo N e non è più garantito
Come funziona il buffer circolare?
- head e tail sono incrementati modulo N
- se la coda è vuota vuol dire che head ha raggiunto tail
- se è piena tail ha raggiunto head
- si allocano maxn+1 elementi in modo che non si possa mai avere head == tail
- per convenzione all’inizio head = N e tail =0
- coda vuota: head%N == tail
ADT QUEUE: implementazione con vettore, quasi ADT. Scrivere queue.h e queue.c con strutture dati e funzioni di inizializzazione, può e get.
- void QUEUEinit(int MAXN);
- int QUEUEempty();
- void QUEUEput(Item val);
- Item QUEUEget();
queue.h
void QUEUEinit(int MAXN);
int QUEUEempty();
void QUEUEput(Item val);
Item QUEUEget();
queue.c
static Item *q;
static int N, head, tail;
void QUEUEinit(int MAXN){
q = malloc((MAXN+1)*sizeof(Item));
N = MAXN+1; head = N; tail = 0;
}
int QUEUEempty() {
return head%N == tail;
}
void QUEUEput(Item val){
q[tail++] = val;
tail = tail%N;
}
Item QUEUEget() {
head = head%N;
return q[head++];
}
ADT QUEUE: implementazione con vettore, ADT di I classe. Scrivere queue.h e queue.c con strutture dati e funzioni di inizializzazione, può e get.
- QUEUE QUEUEinit(int MAXN);
- int QUEUEempty(QUEUE q);
- void QUEUEput(QUEUE q, Item val);
- Item QUEUEget(QUEUE q);
queue.h
typedef struct queue *QUEUE;
QUEUE QUEUEinit(int MAXN);
int QUEUEempty(QUEUE q);
void QUEUEput(QUEUE q, Item val);
Item QUEUEget(QUEUE q);
queue.c
struct queue { Item *q; int N, head, tail; };
QUEUE QUEUEinit(int MAXN){
QUEUE q = malloc(sizeof(q));
q->q = malloc((maxN+1)sizeof(Item));
q->N = maxN+1;
q->head = N;
q->tail =0;
return q;
}
int QUEUEempty(QUEUE q){
return (q->head)%(q->N) == (q->tail);
}
void QUEUEput(QUEUE q, Item val){
q->q[tail++] = val;
q->tail = q->tail%N;
}
Item QUEUEget(QUEUE q){
q->head = (q->head)%N;
return q->q[q->head++];
}
ADT QUEUE: implementazione con lista, quasi ADT. Scrivere queue.h e queue.c con strutture dati e funzioni di inizializzazione, put e get.
- void QUEUEinit(int MAXN);
- int QUEUEempty();
- void QUEUEput(Item val);
- Item QUEUEget();
queue.h
void QUEUEinit(int MAXN);
int QUEUEempty();
void QUEUEput(Item val);
Item QUEUEget();
queue.c
typedef struct QUEUEnode *link;
struct QUEUEnode { Item val; link next; };
static link head, tail;
link NEW(Item val, link next){
link x = malloc(sizeof(*x));
x->val = val; x->next = next;
return x;
}
int QUEUEempty(){
return head == NULL;
}
void QUEUEput(Item val){
if (head == NULL) head = tail = NEW(val, head);
else {
tail ->next = NEW(val, tail->NEXT);
tail = tail->next;
}
}
Item QUEUEget(){
Item tmp;
tmp = head->val;
link t = head->next;
free(head);
head = t;
return tmp;
}
ADT QUEUE: implementazione con lista, ADT di I classe. Scrivere queue.h e queue.c con strutture dati e funzioni di inizializzazione, può e get.
- QUEUE QUEUEinit(int MAXN);
- int QUEUEempty(QUEUE q);
- void QUEUEput(QUEUE q, Item val);
- Item QUEUEget(QUEUE q);
queue.h
typedef struct queue *QUEUE;
QUEUE QUEUEinit(int MAXN);
int QUEUEempty(QUEUE q);
void QUEUEput(QUEUE q, Item val);
Item QUEUEget(QUEUE q);
queue.c
typedef struct QUEUEnode *link;
struct QUEUEnode { Item val; link next; };
struct queue {link head, tail; };
QUEUE QUEUEinit(int MAXN){
QUEUE q = malloc(sizeof(*q));
q->head = NULL;
return q;
}
int QUEUEempty(QUEUE q){ return q->head = NULL; }
void QUEUEput(QUEUE q, Item val){
if (q->head == NULL){
q->tail = NEW(val, q->head);
q->head = q->tail;
}
else {
q->tail->next = NEW(val, q->tail->next);
q->tail = q->tail->next;
}
}
Item QUEUEget(QUEUE q){
Item tmp = q->head->val;
link t = q->head->next;
free(q->head); q->head = t;
return tmp;
}
ADT PQ: implementazione con vettore/lista non ordinato
Complessità di:
- inserzione in testa alla lista o in coda al vettore
- estrazione del max/min
- cambio di priorità
- inserzione in testa alla lista o in coda al vettore: O(1)
- estrazione del max/min O(N)
- cambio di priorità O(N)
ADT PQ: implementazione con vettore/lista ordinato
Complessità di:
- inserzione ordinata
- estrazione del max/min
- cambio di priorità
- inserzione ordinata O(N)
- estrazione del max/min O(1)
- cambio di priorità O(N)
ADT PQ: implementazione con heap di item/indici
Complessità di:
- inserzione
- show max/min
- estrazione max/min
- inserzione O(logN)
- show max/min O(1)
- estrazione max/min O(logN)
ADT PQ: implementazione con lista ordinata, ADT I classe (max in prima posizione).
Scrivere i PQ.h e PQ.c per le strutture dati e le funzioni:
- PQ PQinit(int maxN);
- int PQempty(PQ pq);
- void PQinsert(PQ pq, Item data);
- Item PQextractmax(PQ pq);
- Item PQshowmax(PQ pq);
- void PQdisplay(PQ pq);
- void PQchange(PQ pq, Item val);
PQ.h
typedef struct pqueue *PQ;
PQ PQinit(int maxN);
int PQempty(PQ pq);
void PQinsert(PQ pq, Item data);
Item PQextractmax(PQ pq);
Item PQshowmax(PQ pq);
void PQdisplay(PQ pq);
void PQchange(PQ pq, Item data);
PQ.c
typedef struct PQnode *link;
struct PQqueue { Item val; link next; };
struct pqueue {link head; };
PQ PQinit(int maxN){
PQ pq = malloc(sizeof(*pq));
pq->head = NULL;
return pq;
}
int PQempty(PQ pq) {
return pq->head == NULL;
}
void PQinsert(PQ pq, Item data){
link x, p;
Key k = KEYget(val);
if(pq->head == NULL || KEYless(k, KEYget(pq->head->val))){
pq->head = NEW(val, pq->head);
return;
}
else{
for(x = pq->head->next, p = pq->head; x !§= NULL && KEYless(k, KEYget(x->val)); p=x, x=x->next);
p->next = NEW(val, x);
return;
}
}
Item PQextractmax(PQ pq){
Item tmp;
tmp = pq->head->val;
link t = pq->head->next;
free(pq->head);
pq->head = t;
return tmp;
}
Item PQshowmax(PQ pq){
return pq->head->val;
}
void PQdisplay(PQ pq){
link x;
for (x = pq->head; x != NULL; x = x->next) ITEMdispaly(x->val);
return;
}
void PQchange(PQ pq, Item val){
link x, p;
for (x = pq->head, p = NULL; x != NULL; p = x, x = x->next){
if(ITEMeq(x->val, val)){
if(x == pq->head) pq->head = x->next;
else p->next = x->next;
free(x);
break;
}
}
PQinsert(pq, val);
return;
}
Definizione di Heap
Albero binario con:
- proprietà strutturale: quasi completo (da sx a dx) e quasi bilanciato
- proprietà funzionale: per ogni i ≠ r si ha che Key(Parent(i)) ≥ Key(i)
HEAP: che tipo di Item si usa?
tipologia 3:
- char * name
- int value
- chiave che punta a name
HEAP: come si implementa?
ADT di I classe, puntatore h
- h-> A vettore di Item
- h->heapsize numero di elementi
- per ogni dato i del vettore, il figlio sx si trova in 2i+1, quello destro in 2i+2, il padre in (i-1)/2
HEAP: scrivere i file heap.h e heap.c (prima parte) con le relative strutture dati e funzioni (quasi ADT o ADT di prima classe?):
- Heap HEAPinit(int maxN);
- void HEAPfree(Heap h);
- void HEAPfill(Heap h, Item val);
- void HEAPdisplay(Heap h);
ADT DI PRIMA CLASSE
heap.h
typedef struct heap *Heap;
Heap HEAPinit(int maxN);
void HEAPfree(Heap h);
void HEAPfill(Heap h, Item val);
void HEAPdisplay(Heap h);
heap.c
struct heap { Item *A; int heapsize; }
int LEFT(int i) {return (i2+1);}
int RIGHT(int i) {return (i2+2);}
int PARENT(int i) {return ((i-1)/2);}
Heap HEAPinit(int maxN){
Heap h = malloc(sizeof(h));
h->A = malloc(maxNsizeof(Item));
h->heaapsize = 0;
return h;
}
void HEAPfree(Heap h){
free(h->A); free(h);
}
void HEAPfill(Heap h, Item val) {
int i;
i = h->heapsize++;
h->A[i] = val;
return;
}
void HEAPdisplay(Heap h){
for(int i = 0; i< h->heapsize; i++) ITEMstore(h->A[i]);
}
HEAP: scrivere il file heap.c (seconda parte) con le funzioni:
- void HEAPify(HEAP h, int i);
- void HEAPbuild(Heap h);
- void HEAPsort(Heap h);
void HEAPify(HEAP h, int i){
int l, r, largest;
l = LEFT(i); r = RIGHT(i);
if (l < h->heapsize && KEYcompare(KEYget(h->A[l]), (KEYget(h->A[i])) == 1)
largest = l;
if (r < h->heapsize && KEYcompare(KEYget(h->A[r]), (KEYget(h->A[i])) == 1)
largest = r;
}
if (largest != i){
Swap(h, i, largest);
HEAPify(h, largest);
}
}
void HEAPbuild(Heap h){
int i;
for(i = PARENT(h->heapsize-1); i > 0; i–)
HEAPify(h, i);
}
void HEAPsort(Heap h){
int i, j;
HEAPbuild(h);
j = h->heapsize;
for(i = h->heapsize-1; i>0; i–){
Swap(h, 0, i);
h->heapsize–;
HEAPify(h, 0);
}
h->heapsize = j;
}
HEAP: complessità HEAPbuild
Per ogni valore dell’heap, quindi per n volte, viene chiamata la HEAPify che ha complessità log_2(n).
L’equazione alle ricorrenze è T(n) = log_2(n) + 2T(n/2)
dove a = 2 sono i sottoalberi (sottoproblemi) e b = 2 sono i dati dimezzati del sottoproblema
Lo sviluppo per unfolding porta a O(n)
HEAP: complessità HEAPsort e caratteristiche
O(nlogN)
in loco
stabile
Completa la tabella
PQinsert PQshowmax PQextractmax vettore non ordinato lista non ordinata vettore ordinato lista ordinata heap di item/indici
PQinsert PQshowmax PQextractmax
vettore non ordinato 1 O(n) O(n)
lista non ordinata 1 O(n) O(n)
vettore ordinato O(n) 1 1
lista ordinata O(n) 1 1
heap di item/indici O(long) 1 logN
PQ: implementazione con HEAP, ADT I classe, vettore di Item
scrivere PQ.h e PQ.c delle funzioni:
- PQ PQinit(int maxN);
- void PQfree(PQ pq);
- int PQempty(PQ pq);
- int PQsize(PQ pq);
- void PQinsert(PQ pq, Item val);
- Item PQshowmax(PQ pq);
- Item PQexctractmax(PQ pq); !!!!
- void PQdisplay(PQ pq);
PQ.h
typedef struct pqueue *PQ;
// tutte le funzioni della domanda
PQ.c
struct pqueue {Item *A; int heapsize; };
// funzioni di LEFT RIGHT PARENT
PQ PQinit(int maxN){
PQ p = malloc(sizeof(*p));
p->A = (Item )malloc(maxNsizeof(Item));
p->heapsize = 0;
return p;
}
void PQfree(PQ pq) {
free(pq->A); free(pq);
}
int PQempty(PQ pq) { return pq->heapsize == 0; }
int PQsize(PQ pq) { return pq->heapsize; }
void PQinsert(PQ pq, Item val) {
int i = pq->hepsize++;
Key k = KEYget(val);
while(i≥1 && KEYcompare (KEYget(pq->A[PARENT(i)]), k) == -1) {
pq->A[i] = pq->A[PARENT(i)];
i = PARENT(i);
}
pq->A[i] = val;
return;
}
Item PQshowmax(PQ pq) { return pq->A[0]; }
Item PQexctractmax(PQ pq) {
Item val;
Swap(pq, 0, pq->heapsize-1);
val = pq->A[pq->hepsize-1];
pq->heapsize–;
HEAPify(pq, 0);
return val;
}
void PQdisplay(PQ pq) {
for (int i = 0; i < pq->heapsize; i++) ITEMstore(pq->A[i]);
}
PQ: implementazione con HEAP, ADT I classe, vettore di Item
scrivere PQ.c della funzione
- void PQchange(PQ pq, Item val);
void PQchange (PQ pq, Item val) {
int i, found = 0, pos;
for (i = 0; i < pq->heapsize && found == 0; i++)
if (NAMEcmp(NAMEget(&(pq->A[i])), NAMEget(&val))==0) {
found = 1;
pos = i;
}
if (found==1) {
while(pos>=1 && PRIOget(pq->A[PARENT(pos)]) < PRIOget(val)){
pq->A[pos] = pq->A[PARENT(pos)];
pos = PARENT(pos);
}
pq->A[pos] = val;
HEAPify(pq, pos);
}
else printf(“key not found!\n”);
return;
}
PQ: implementazione con HEAP, ADT I classe, vettore di indici
scrivere PQ.h e PQ.c delle funzioni:
- PQ PQinit(int maxN);
- void PQfree(PQ pq);
- int PQempty(PQ pq);
- int PQsize(PQ pq);
- void PQinsert(PQ pq, int index, int prio);
- Item PQshowmax(PQ pq);
- Item PQexctractmax(PQ pq);
- static void Swap(PQ pq, int pos1, int pos2); (solo .c)
- static void HEAPify(PQ pq, int i); (solo .c)
PQ.h
typedef struct pqueue *PQ;
// funzioni
PQ.c
typedef struct { int index; int prio; } heapItem;
struct pqueue { heapItem *A; int heapsize; int *qp};
PQ PQinit(int maxN){
PQ p = malloc(sizeof(p));
p->A = (heapItem )malloc(maxNsizeof(heapItem));
p->qp = malloc(maxNsizeof(int));
for (int i = 0; i < maxN; i++) {
p->A[i].index = -1; p->qp[i] = -1;
}
p->heapsize = 0;
return p;
}
void PQfree(PQ pq) {
free(pq->qp); free(pq->A); free(pq);
}
int PQempty(PQ pq) { return pq->heapsize == 0; }
int PQsize(PQ pq) { return pq->heapsize; }
void PQinsert(PQ pq, int index, int prio) {
int i, j;
i = pq->hepsize++;
Key k = KEYget(val);
while(i≥1 && (pq->A[PARENT(i)].prio) < prio)){
pq->A[i] = pq->A[PARENT(i)];
pq->qp[pq->A[i].index] = I;
i = PARENT(i);
}
pq->A[i].index = index;
pq->A[i].prio = prio;
pq->qp[index] = i;
}
Item PQshowmax(PQ pq) { return pq->A[0]; }
static void Swap(PQ pq, int pos1, int pos2){
heapItem temp;
int index1, index2;
temp = pq->A[pos1];
pq->A[pos1] = pq->A[pos2]; pq->A[pos2] = temp;
// update correspondence index-pos
index1 = pq->A[pos1].index;
index2 = pq->A[pos2].index;
pq->qp[index1] = pos1;
pq->qp[index2] = pos2;
}
static void HEAPify(PQ pq, int i) {
int l, r, largest;
l = LEFT(i);
r = RIGHT(i);
if (l < pq->heapsize && (pq->A[l].prio > pq->A[i].prio))
largest = l;
else
largest = i;
if (r < pq->heapsize && (pq->A[r].prio > pq->A[largest].prio))
largest = r;
if (largest != i) {
Swap(pq, i, largest);
HEAPify(pq, largest);
}
}
Item PQexctractmax(PQ pq) {
int res;
int j=0;
Swap (pq, 0, pq->heapsize-1);
res = pq->A[pq->heapsize-1].index;
pq->qp[res]=-1;
pq->heapsize–;
pq->A[pq->heapsize].index=-1; // redundant
HEAPify(pq, 0);
return res;
}
PQ: implementazione con HEAP, ADT I classe, vettore di indici
scrivere il file PQ.c della funzione
void PQchange (PQ pq, int index, int prio);
void PQchange (PQ pq, int index, int prio) {
int pos = pq->qp[index];
heapItem temp = pq->A[pos];
temp.prio = prio; // new prio
while ((pos>=1) && (pq->A[PARENT(pos)].index < prio) {
pq->A[pos] = pq->A[PARENT(pos)];
pq->qp[pq->A[pos].index] = pos;
pos = PARENT(pos);
}
pq->A[pos] = temp;
pq->qp[index] = pos;
HEAPify(pq, pos);
}
ST: definizione; tipo di item, implementazioni
La tabella di simboli è un ADT che supporta operazioni di Insert, Search, Delete
Si realizza con item di tipo 3 e implementazione di quasi ADT
Le implementazioni più comuni sono le tabelle ad accesso diretto, strutture lineari, BST e varianti, tabelle di hash
ST: complessità generale del caso PEGGIORE per INSERT, SEARCH, SELECT per le strutture dati:
- tabelle ad accesso diretto
- vettori e liste non ordinate
- vettori e liste ordinate
- BST !!!
- tabelle di hash !!!
- tabelle ad accesso diretto: 1, 1, maxN
- vettori e liste non ordinate: 1, N, /
- vettori e liste ordinate N, search nel caso delle liste N mentre nel caso dei vettori con ricerca dicotomica logN, 1
- BST: N, N, N
- tabelle di hash: 1, N, /
ST: implementazione tabella ad accesso diretto, ADT I classe
Come sono identificati gli elementi?
Ogni elemento è identificato univocamente dalla chiave
ST: implementazione tabella ad accesso diretto, ADT I classe
Scrivere il file ST.c per le funzioni:
- ST STinit(int maxN);
- int STcount(ST st);
- void STfree(ST st);
- void STinsert(ST st, Item val);
- Item STsearch(ST st, Key k);
- void STdelete(ST st, Key k);
- void STdisplay(ST st);
struct symbtab {Item *a; int N; int M;};
ST STinit(int maxN) {
ST st;
int i;
st = malloc(sizeof(*st));
st->a = malloc(maxN * sizeof(Item));
for (i = 0; i < maxN; i++) st->a[i] = ITEMsetvoid();
st->M = maxN;
st->N= 0;
return st;
}
int STcount(ST st) { return st->N; }
void STfree(ST st) {
free(st->a);
free(st);
}
void STinsert(ST st, Item val) {
int index = GETindex(KEYget(val));
st->a[index] = val;
st->N++;
}
Item STsearch(ST st, Key k) {
int index = GETindex(k);
return st->a[index];
}
void STdelete(ST st, Key k) {
st->a[GETindex(k)] = ITEMsetvoid();
st->N–;
}
void STdisplay(ST st){
int i;
for (i = 0; i < st->M; i++)
if (ITEMcheckvoid(st->a[i])==0)
ITEMstore(st->a[i]);
}
ST: implementazione tabella ad accesso diretto, ADT I classe
Vantaggi e svantaggi: inserimento, ricerca, cancellazione, inizializzazione, selezione, occupazione di memoria
Inserimento, ricerca e cancellazione T(1)
inizializzazione e selezione Theta(N)
occupazione di memoria Theta(N)
ST: implementazione vettore ordinato e non, ADT I classe
Scrivere il file ST.c per le funzioni:
- ST STinit(int maxN);
- int STcount(ST st);
- void STfree(ST st);
- void STdisplay(ST st);
- void STdelete(ST st, Key k);
vettore non ordinato:
- void STinsert(ST st, Item val);
- Item STsearch(ST st, Key k);
vettore ordinato:
- void STinsert(ST st, Item val);
- Item STselect(ST st, int r);
- Item STsearch(ST st, Key k);
- Item searchR(ST st, int l, int r, Key k);
struct symbtab {Item *a; int maxN; int size;};
ST STinit(int maxN) {
ST st; int i;
st = malloc(sizeof(*st));
st->a = malloc(maxN * sizeof(Item) );
for (i = 0; i < maxN; i++) st->a[i] = ITEMsetvoid();
st->maxN = maxN;
st->size = 0;
return st;
}
int STcount(ST st) { return st->size; }
void STfree(ST st) { free(st->a); free(st); }
void STdisplay(ST st){
int i;
for (i = 0; i < st->size; i++) ITEMstore(st->a[i]);
}
void STdelete(ST st, Key k) {
int i, j=0;
while (KEYcmp(KEYget(&st->a[j]), k)!=0) j++;
for (i = j; i < st->size-1; i++)
st->a[i] = st->a[i+1];
st->size–;
}
Inserzione e ricerca in vettore non ordinato
void STinsert(ST st, Item val) {
int i = st->size;
if (st->size >= st->maxN) {
st->a=realloc(st->a,(2st->maxN)sizeof(Item));
if (st->a == NULL) return;
st->maxN = 2*st->maxN;
}
st->a[i] = val; st->size++;
}
Item STsearch(ST st, Key k) {
int i;
if (st->size == 0) return ITEMsetvoid();
for (i = 0; i < st->size; i++)
if (KEYcmp(k, KEYget(&st->a[i]))==0)
return st->a[i];
return ITEMsetvoid();
}
Inserzione, selezione e ricerca in vettore ordinato
void STinsert(ST st, Item val) {
int i = st->size++;
if (st->size > st->maxN) {
st->a=realloc(st->a,(2st->maxN)sizeof(Item)); if (st->a == NULL)
return;
st->maxN = 2*st->maxN;
} while((i>0)&&KEYcmp(KEYget(&val),KEYget(&st->a[i-1]))==-1){
st->a[i] = st->a[i-1];
i–; }
st->a[i] = val;
}
Item STselect(ST st, int r) { return st->a[r];
}
Item STsearch(ST st, Key k) {
return searchR(st, 0, st->size-1, k) ;
}
Item searchR(ST st, int l, int r, Key k) {
int m;
m = (l + r)/2; if (l > r)
return ITEMsetvoid();
if (KEYcmp(k,KEYget(&st->a[m]))==0)
return st->a[m];
if (l == r)
return ITEMsetvoid();
if (KEYcmp(k, KEYget(&st->a[m]))==-1)
return searchR(st, l, m-1, k); else
return searchR(st, m+1, r, k); }
ST: implementazione vettore ordinato e non, ADT I classe
Vantaggi e svantaggi: inizializzazione, inserimento, ricerca, cancellazione, occupazione di memoria,
Vantaggi/svantaggi (vettore non ordinato)
▪ Complessità dell’operazione di inizializzazione e inserimento: T(n) = O(1)
▪ Complessità delle operazioni di ricerca, cancellazione: T(n) = O(N)
▪ Occupazione di memoria S(n) = Theta(maxN), spreco di memoria per |K| «_space;maxN
Vantaggi/svantaggi (vettore ordinato)
▪ ogni inserzione ordinata ha costo lineare T(n) = O(N), costo quadratico complessivo per N inserzioni T(n) = O(N2)
▪ ricerca dicotomica con costo logaritmico T(n) = O(logN)
▪ ricerca lineare con interruzione non appena possibile T(n) =
O(N)
▪ cancellazione con costo lineare T(n) = O(N)
▪ selezione banale: rango e indice coincidono -> O(1)
ST: implementazione lista ordinata e non, ADT I classe
Scrivere il file ST.c per le funzioni:
- ST STinit(int maxN);
- void STfree(ST st);
- int STcount(ST st);
- void STdisplay(ST st);
- Item STsearch(ST st, Key k);
- void STdelete(ST st, Key k);
- Inserzione in lista non ordinata
void STinsert(ST st, Item val);
- Selezione in lista ordinata
Item STselect(ST st, int r);
- Inserzione in lista ordinata void STinsert(ST st, Item val);
typedef struct STnode* link;
struct STnode { Item val; link next; } ; typedef struct { link head; int size; } list; struct symbtab { list tab; };
static link NEW( Item val, link next) {
link x = malloc(sizeof *x);
if (x == NULL) return NULL;
x->val = val; x->next = next;
return x;
}
ST STinit(int maxN) {
ST st;
st = malloc(sizeof(*st));
if (st == NULL) return NULL;
st->tab.size = 0;
st->tab.head = NULL;
return st;
}
void STfree(ST st) {
link x, t;
for (x = st->tab.head; x != NULL; x = t) {
t = x->next;
free(x);
}
free(st);
}
int STcount(ST st) {
return st->tab.size;
}
void STdisplay(ST st) {
link x;
for (x = st->tab.head; x != NULL; x = x->next)
ITEMstore(x->val);
}
Item STsearch(ST st, Key k) {
link x;
if (st == NULL)
return ITEMsetvoid();
if (st->tab.head == NULL)
return ITEMsetvoid();
for (x = st->tab.head; x != NULL; x = x->next)
if (KEYcmp( KEYget(&x->val), k) ==0)
return x->val;
return ITEMsetvoid();
}
void STdelete(ST st, Key k) {
link x, p;
if (st == NULL) return;
if (st->tab.head == NULL) return;
for (x=st->tab.head, p=NULL; x!=NULL; p=x, x=x->next) { if (KEYcmp(k, KEYget(&x->val)) == 0) {
}
Inserzione in lista non ordinata
void STinsert(ST st, Item val) {
if (st == NULL) return;
st->tab.head = NEW(val, st->tab.head);
st->tab.size++;
}
Selezione in lista ordinata
Item STselect(ST st, int r) { int i;
link x = st->tab.head;
for (i = r; i>0; i–) x = x->next; return x->val;
}
void STinsert(ST st, Item val) {
link x, p;
if (st == NULL)
return;
if ((st->tab.head == NULL) || (KEYcmp(KEYget(&st->tab.head->val),KEYget(&val))==1))
st->tab.head = NEW(val,st->tab.head);
else {
for (x = st->tab.head->next, p = st->tab.head;
x!=NULL&& (KEYcmp(KEYget(&val),KEYget(&x->val))==1); p = x, x = x->next);
p->next = NEW(val, x);
}
st->tab.size++; }
ST: implementazione lista ordinata e non, ADT I classe
Vantaggi e svantaggi: inizializzazione, inserimento, ricerca, cancellazione, selezione, occupazione di memoria
complessità dell’operazione di inizializzazione e inserimento: T(n) = O(1) (inserimento in lista ordinata T(n) = O(n))
▪ complessità delle operazioni di ricerca e cancellazione: T(n) = O(n)
▪ complessità dell’operazione di selezione con lista ordinata: T(n) = O(n)
▪ occupazione di memoria S(n) = Theta(n)
BST: qual è il loro vantaggio rispetto alle altre strutture dati?
Permettono di fare operazioni con costo logn