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


Codici commentati di Algoritmi, Appunti di Algoritmi E Strutture Di Dati

Descrizione e codici commentati di Algoritmi

Tipologia: Appunti

2017/2018

Caricato il 19/03/2018

alexandro.vassallo
alexandro.vassallo 🇮🇹

6 documenti

1 / 54

Toggle sidebar

Questa pagina non è visibile nell’anteprima

Non perderti parti importanti!

bg1
1
CODICI GENERICI
Funzione che fa la moltiplicazione tra due interi
int mult(int a, int b) {
int res = 0;
while (b) {
if (b&1)
res += a;
a <<= 1;
b >>=1;
}
return res;
}
Calcolo dell’entropia
/* La funzione entropia ci dà una stima della casualità di un esperimento aleatorio.
L’entropia normalizzata dà valori compresi tra 0 e 1. Più il valore risultante della
funzione è vicino a 1, più bilanciato sarà l’esperimento. La funzione entropia è
universale. In questo caso passiamo come argomento alla funzione un vettore di sei double
che contengono le frequenze delle uscite delle facce di un dado. Per molti lanci la
funzione dà come risultato 1, per pochi lanci dà un risultato sensibilmente minore di 1
perché non si può avere un bilanciamento perfetto. */
double entropia(double* v, int dim) {
int i;
double s = 0;
for (i=0; i<dim; i++) {
if (v[i] == 0) continue;
s -= v[i]*log(v[i]);
}
return (s/log(dim));
}
int main() {
double dado[6];
int i, lanci = 1000;
dado[0] = 0.8;
dado[1] = 0.2;
dado[2] = dado[3] = dado[4] = dado[5] = dado[6] = 0;
printf(“%3.2lf\n\n\n”, entropia(dado, 6); //caso sbilanciato
srand(time(NULL));
for (i=0; i<6; i++)
dado[i] = 0;
for (i=0; i<lanci; i++)
(dado[rand()%6])++;
for (i=0; i<6; i++)
dado[i+ = ((double) dado[i])/lanci;
printf(“%3.2lf\n”, entropia(dado, 6)); //caso bilanciato
system(“pause”);
return 0;
}
Funzione Hanoi
void hanoi(char p1, char p2, char p3, int n) {
if (n==1) printf(“sposta da %c a %c\n”, p1, p3);
else {
hanoi(p1, p3, p2, n-1);
hanoi(p1, p2, p3, 1);
hanoi(p2, p1, p3, n-1);
}
}
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e
pf1f
pf20
pf21
pf22
pf23
pf24
pf25
pf26
pf27
pf28
pf29
pf2a
pf2b
pf2c
pf2d
pf2e
pf2f
pf30
pf31
pf32
pf33
pf34
pf35
pf36

Anteprima parziale del testo

Scarica Codici commentati di Algoritmi e più Appunti in PDF di Algoritmi E Strutture Di Dati solo su Docsity!

CODICI GENERICI

Funzione che fa la moltiplicazione tra due interi

int mult(int a, int b) { int res = 0; while (b) { if (b&1) res += a; a <<= 1; b >>=1; } return res; }

Calcolo dell’entropia

/* La funzione entropia ci dà una stima della casualità di un esperimento aleatorio. L’entropia normalizzata dà valori compresi tra 0 e 1. Più il valore risultante della funzione è vicino a 1, più bilanciato sarà l’esperimento. La funzione entropia è universale. In questo caso passiamo come argomento alla funzione un vettore di sei double che contengono le frequenze delle uscite delle facce di un dado. Per molti lanci la funzione dà come risultato 1, per pochi lanci dà un risultato sensibilmente minore di 1 perché non si può avere un bilanciamento perfetto. */

double entropia(double* v, int dim) { int i; double s = 0; for (i=0; i<dim; i++) { if (v[i] == 0) continue; s -= v[i]*log(v[i]); } return (s/log(dim)); }

int main() { double dado[6]; int i, lanci = 1000; dado[0] = 0.8; dado[1] = 0.2; dado[2] = dado[3] = dado[4] = dado[5] = dado[6] = 0; printf(“%3.2lf\n\n\n”, entropia(dado, 6); //caso sbilanciato srand(time(NULL)); for (i=0; i<6; i++) dado[i] = 0; for (i=0; i<lanci; i++) (dado[rand()%6])++; for (i=0; i<6; i++) dado[i+ = ((double) dado[i])/lanci; printf(“%3.2lf\n”, entropia(dado, 6)); //caso bilanciato system(“pause”); return 0; }

Funzione Hanoi

void hanoi(char p1, char p2, char p3, int n) { if (n==1) printf(“sposta da %c a %c\n”, p1, p3); else { hanoi(p1, p3, p2, n-1); hanoi(p1, p2, p3, 1); hanoi(p2, p1, p3, n-1); } }

INSIEMI

/Possiamo rappresentare gli insiemi in c usando semplicemente un unsigned int (4 byte). Usare gli unsigned unsigned ci permette di considerare ogni bit come rappresentativo di un elemento di un insieme. Quindi il bit i-mo dice che l’elemento i-mo è presente all’interno del set. Per identificare gli elementi possiamo usare un vettore di stringhe a parte./

typedef unsigned long SET;

SET unione(SET a, SET b) { return a | b; }

SET intersezione(SET a, SET b) { return a & b; }

int presente(SET a, int k) { return ((a >> k) & 1); //restituisce 1 se l’elemento k-mo è //presente, 0 altrimenti }

void printset(SET a) { int k; for (k=31; k>=0; k--) printf(“%d”, ((a>>k) & 1)); putchar(‘\n’); }

/* Se abbiamo bisogno di rappresentare più dei 32 elementi di un solo unsigned long, possiamo creare un vettore di unsigned long (che possiamo chiamare myset) e creare una funzione che, a seconda del numero dell’elemento che ci interessa, trovi quale specifico intero del vettore e quale posizione controllare in esso */

int ricerca(SET* v, int k) { int i, j; i = k /32; j = k % 32; return ((v[i] >> j) & 1); //restituisce 1 se l’elemento è presente }

/* la funzione getCharSet crea un insieme che indica quali caratteri sono presenti all’interno di una stringa */

SET getCharSet(char* str) { char c; SET s = 0; while (c = toupper(*str++)) { //toupper considera tutte le lettere //come maiuscole s |= 1 << (c – ‘A’); // 1 << (c – ‘A’) è una maschera che // ha tutti 0 e un solo bit a 1 //(quello corrispondente al numero //della lettera considerata. //Facendone l’or con s, aggiungiamo // a s tale bit. } return s; }

/* La funzione checkStraniere controlla se in una stringa sono presenti caratteri stranieri (in tal caso restituisce il valore 1) */

int checkStraniere(char *str) { SET a, b; a = getCharSet(str); //crea l’insieme dei caratteri di str b = getCharSet(“jkwxy”); //crea l’insieme dei caratteri stranieri return (a&b); //è = 0 se a e b non hanno bit in comune

LIBRERIA MATRICI E COMPLESSI

/* Creiamo una serie di funzioni che ci permettono di gestire le matrici. Per prima cosa abbiamo le funzioni di allocazione della memoria necessaria per la gestione di matrici */

double** matalloc(int nrows, int ncols) { //matalloc specializzata per double int i; double** mat; mat = (double) calloc(nrows, sizeof(double)); for (i=0; i<nrows; i++) mat[i] = (double) calloc(ncols, sizeof(double)); return mat; }

char** mymatalloc(int nrows, int ncols, int typesize) { int i; char** mat; mat = (char) calloc(nrows, sizeof(char)); for (i=0; i<nrows; i++) mat[i] = (char) calloc(ncols, typesize); return mat; }

/*funzione che moltiplica una matrice per un vettore */

void matxvect(int dim, int ncols, double** m, doublev, double res) { int i, j; for (i=0; i<dim; i++) { res[i] = 0.0; for (j=0; j<ncols;j++) res[i] += m[i][j] * v[j]; } }

/*prodotto matriciale, con matrice risultata passata per argomento alla funzione (e quindi già preallocata nell’ambiente chiamante */

void matxmat(int nr1, int dim, int nc2, double** m1, double** m2, double** res) { int i, j, k; for (i=0; i<nr1; i++) for (j=0; j<nc2; j++) res[i][j] = 0.0; for (k=0; k<dim; k++) res[i][j] += m1[i][k]*m2[k][j]; } }

/*Seconda versione del prodotto matriciale, che alloca internamente la matrice risultato e la restituisce come puntatore doppio a double */

double** callmatxmat(int nr1, int dim, int nc2, double** m1, double** m2) { int i, j, k; double** res; res = (double*) mymatalloc(nr1, nr2, sizeof(double)); for (i=0; i<nr1; i++) for (j=0; j<nc2; j++) res[i][j] = 0.0; for (k=0; k<dim; k++) res[i][j] += m1[i][k]m2[k][j]; } }

/* funzione che stampa una matrice */

void matprint (double** mat, int nr, int nc) { int i, j; for (i=0; i<nr; i++){

for (j=0; j<nc; j++) printf(“%3.2lf\t”, mat[i][j]); putchar(‘\n’); } putchar(‘\n’); }

/* funzione che riempie una matrice di elementi casuali */

void matrand (double** mat, int nr, int nc) { int i, j; for (i=0; i<nr; i++) for (j=0; j<nc; j++) mat[i][j] = ((double) rand())/RAND_MAX; //valori real tra 0 e 1 return; }

/* Libreria di funzioni sui numeri complessi */

typedef struct complex { double re; double im; } Complex;

Complex sum (Complex a, Complex b) { Complex c; c.re = a.re + b.re; c.im = a.im + b.im; return c; }

Complex prod(Complex a, Complex b) { Complex c; c.re = a.reb.re – a.imb.im; c.im = a.imb.re + a.reb.im; return c; }

Complex moduloquadro(Complex a) { return (a.rea.re + a.ima.im); }

Complex fase(Complex a) { Return (atan(-a.im/a.re)); }

/* funzioni per matrici di complessi */

void matcrand(Complex** mat, int nrows, int ncols) { int i, j; for (i=0; i<nrows; i++) for (j=0; j<ncols; j++) { mat[i][j].re = ((double) rand())/RAND_MAX; mat[i][j].im = ((double) rand())/RAND_MAX; } }

void matcprint(Complex** mat, int nrows, int ncols) { int i, j; for (i=0; i<nrows; i++) { for (j=0; j<ncols; j++) printf(“%3.2lf + %3.2lf i \t”, mat[i][j].re, mat[i][j].im); putchar(‘\n’); } }

Complex** matcprod(Complex** mat1, Complex** mat2, int nrows1, int dim, int ncols2) { int i, j, k; Complex** res, tmp;

SIMULAZIONE DI MACCHINE A STATI FINITI

/* Una FSM (macchina a stati finiti) si può simulare utilizzando una matrice di interi, in cui le righe corrispondono agli input, le colonne allo stato corrente, la casella indica lo stato successivo. Se si fa una rete di tipo Mealy, bisogna fare un’altra matrice che in ogni casella indica l’output corrispondente a una coppia i-j (stato- ingresso). Per una rete di tipo Moore non è necessaria una matrice per gli outputs */

typedef struct { int** nextstates; int** outputs; } FSM;

int** matalloc(int nrows, int ncols) { int i; int** m; m = (int) calloc(nrows, sizeof(int)); for (i=0; i<nrows; i++) m[i] = (int) calloc(ncols, sizeof(int)); return m; }

/* A seconda della macchina a stati finiti che vogliamo realizzare, useremo una diversa funzione che inizializza la struttura dati FSM. */

/* Esempio 1: AB+C */

#define A 0 #define B 1 #define C 2 #define D 3

void fsminit(FSM* fsm, int inputs, int states) { //inputs = 4, states = 3 int i, j; fsm->nextstates = matalloc(inputs, states); fsm->outputs = matalloc(inputs, states); fsm->nextstates[A][0] = 1; fsm->nextstates[A][1] = 1; fsm->nextstates[A][2] = 1; fsm->nextstates[B][0] = 0; fsm->nextstates[B][1] = 2; fsm->nextstates[B][2] = 2; fsm->nextstates[C][0] = fsm->nextstates[C][1] = fsm->nextstates[C][2] = 0; fsm->nextstates[D][0] = fsm->nextstates[D][1] = fsm->nextstates[D][2] = 0; for (i=0; i<inputs; i++) for (j=0; j<states; j++) fsm->outputs[i][j] = 0; fsm->outputs[C][2] = 1; return; }

void dofsm(FSM fsm, int* inputs, int ninp) { //simula la fsm, *inputs è un vettore che //contiene tutti gli input (nel nostro caso //saranno lettere che grazie alla define //rappresentano interi) e il numero di //input int as = 0, ns, out; //actual state, next state, output int i; char inp; for (i=0; i<ninp; i++) { //itera per tutti gli input del vettore ns = fsm.nextstates[inputs[i]][as]; out = fsm.outputs[inputs[i]][as]; inp = inputs[i] + 65; //converte l’input numerico nel carattere //corrispondente printf(“AS=%d\n Inp=%c\n NS=%d\n Out=%d\n\n”, as, inp, ns, out); as = ns; }

return; }

/* ESEMPIO 2: 011+0 */

void fsminit(FSM* fsm, int inputs, int states) { //inputs = 2, states = 4 int i, j; fsm->nextstates = matalloc(inputs, states); fsm->outputs = matalloc(inputs, states); fsm -> nextstates[0][0] = fsm -> nextstates[0][1] = 1; fsm -> nextstates[0][2] = fsm -> nextstates[0][3] = 1; fsm -> nextstates[1][0] = 0; fsm -> nextstates[1][1] = 2; fsm -> nextstates[1][2] = 3; fsm -> nextstates[1][3] = 3; for (i=0; i<inputs; i++) for (j=0; j<states; j++) fsm->outputs[i][j] = 0; fsm -> outputs[0][3] = 1; return; }

void dofsm(FSM fsm, int* inputs, int ninp) { //uguale alla precedente, senza che si ha //la necessità di usare una variabile char //inp int as = 0, ns, out; int i; for (i=0; i<ninp; i++) { ns = fsm.nextstates[inputs[i]][as]; out = fsm.outputs[inputs[i]][as]; printf(“AS=%d\n Inp=%d\n NS=%d\n Out=%d\n\n”, as, inputs[i], ns, out); as = ns; } return; }

/* ESEMPIO 3: conta parole realizzato con una fsm */

void smini(FSM* fsm) { //2 ingressi: 0 rappresenta che è //arrivato un numero o una lettera, //1 rappresenta un altro carattere //servono soltanto 2 stati int i, j; fsm->nextstates = matalloc(2, 2); fsm->outputs = matalloc(2,2); fsm->nextstates[0][0] = fsm->nextstates[0][1] = 1; fsm->nextstates[1][0] = fsm->nextstates[1][1] = 0; fsm->outputs[0][0] = 1; fsm->outputs[0][1] = fsm->outputs[1][0] = fsm->outputs[1][1] = 0; return; }

int dofsm(FSM fsm, char* str) { //la funzione dofsm analizza una stringa int as = 0, ns, inp, out; int i, conteggio = 0; //conteggio conterrà il numero di parole char c; while (c = *str++) { if (isdigit(c) || isalpha(c)) inp = 0; else inp = 1; ns = fsm.nextstates[inp][as]; out = fsm.outputs[inp][as]; if (out == 1) conteggio++; //a ogni output 1 aumenta il conteggio as = ns; } return conteggio; }

int doublecompare(char* a, char* b) { //specializzazione per double double da, db; da = (double) a; db = (double) b; if ((da) > (db)) return 1; else if ((da) < (db)) return -1; return 0; }

int complexcompare(char* a, char* b) { //specializzazione per complessi (modulo) COMPLEX ca, cb; double ma, mb, fa, fb; ca = (COMPLEX) a; cb = (COMPLEX) b; ma = moduloquadro(ca); mb = moduloquadro(cb); fa = fase(ca); fb = fase(cb); if (ma < mb) return -1; else if (ma > mb) return 1; else { if (fa < fb) return -1; else if (fa > fb) return 1; } return 0; }

Fastsearch

/* Fastsearch è un algoritmo di ricerca in grado di operare su vettori già ordinati con un algoritmo di ordinamento (questo perché su vettori non ordinati l’unica possibilità è quella di fare una scansione sequenziale, con complessità computazionale lineare. Fastsearch permette una ricerca dicotomica, che ha quindi complessità computazionale logaritmica. La funzione controlla l’elemento centrale e poi si applica in maniera ricorsiva a una delle due metà del vettore. Se trova l’elemento cercato, restituisce il suo indice, altrimenti restituisce il valore non valido -1. */

/* versione specializzata per vettori di interi */

int fastsearch(int v[], int a, int b, int val) { int c; if (b < a) return -1; //caso banale in cui si ritorna - c = (a+b)/2; //c viene settato all’indice centrale if (v[c] == val) return c; //valore trovato: si ritorna l’indice c if (val > v[c]) return (fastsearch(v, c+1, b, val)); //ricerca nella metà destra else return (fastsearch(v, a, c-1, val)); //ricerca nella metà sinistra }

/* versione universale */

int ufastsearch(char* v, int a, int b, char* pval, int size, int (compare)()) { int c, cmp; //cmp conterrà il risultato dei confronti if (b < a) return -1; //caso banale: valore non trovato c = (a+b)/2; cmp = (compare)(pval, v+size*c); if (cmp == 0) return c; else if (cmp == -1) return ufastsearch(v, a, c-1, pval, size, compare); else return ufastsearch(v, c+1, b, pval, size, compare);

Quicksort

/* E’ un algoritmo di ordinamento con complessità nlogn, tanto utilizzato da essere presente nella libreria standard. La complessità è dovuta al fatto che si deve fare una chiamata ricorsiva di profondità logn */

/* versione specializzata per interi */

void quicksort(int a[], int lo, int hi) { //lo e hi sono inizialmente gli estremi int i=lo, j=hi, h; int x = a[(lo+hi)/2]; //x è il valore all’indice centrale; do { while (a[i] < x) i++; //aumenta i fino a quando i valori a //sinistra di x sono minori di x while (a[j] > x) j--; //diminuisce j fino a quando i valori a //destra di x sono maggiori di x if (i<=j) { //scambia i valori all’indice i e j h = a[i]; a[i] = a[j]; a[j] = h; i++; j--; } } while (i<=j); if (lo < j) quicksort(a, lo, j); //applica ricorsivamente quicksort //a sinistra dell’attuale j if (i < hi) Quicksort(a, i, hi); //applica ricorsivamente quicksort //a destra dell’attuale i; le due chiamate //non sono alternative (in quel caso //avremmo una complessità logaritmica) }

/versione universale (usa sempre le funzioni compare specializzate)/

void uquicksort(char* a, int lo, int hi, int size, int (compare)()) { int i=lo, j=hi, k; char h; int c = (lo+hi)/2; char x = (a + (sizec)); do { while (( (compare)((a+sizei), x) ) == -1) i++; while (( (compare)((a+sizej), x) ) == 1) j--; if (i <= j) { for (k=0; k<size;k++) { //swap byte per byte h = a[isize+k]; a[isize+k] = a[jsize+k]; a[j*size+k] = h; } i++; j--; } } while (i<=j); if (lo < j) //riapplica ricorsivamente alla metà uquicksort(a, lo, j, size, compare); //alla sinistra dell’attuale j if (i < hi) //riapplica ricorsivamente alla metà uquicksort(a, i, hi, size, compare); //alla destra dell’attuale i

STACK

/* Lo stack è una struttura dati LIFO, ovvero in cui l’ultimo dato inserito è anche il primo a essere letto. Nello stack si hanno due funzioni principali: la push, che inserisce un dato, e la pop, che ne estrae uno. Lo stack può essere gestito tramite una struttura dati che contiene al suo interno un vettore (che conterrà i valori presenti all’interno dello stack) e due interi (la dimensione e l’indice corrente). Bisogna creare dei controlli che evitano lo stack overflow (più valori della dimensione massima) o lo stack underflow (tentativo di lettura da uno stack vuoto) */

typedef struct { //dichiarazione di una struttura stack double* s; int sindex, size; } Stack;

Stack stack; //dichiarazione di una variabile globale

void init(int size) { //inizializzazione dello stack stack.size = size; stack.s = (double*) calloc(size, sizeof(double)); stack.sindex = 0; }

void push(double val) { if (stack.sindex < stack.size) { stack.s[stack.sindex] = val; stack.sindex++; } else fprintf(stderr, “stack overflow!\n”); }

double pop() { if (stack.sindex > 0) return stack.s[--stack.sindex]; else { fprintf(stderr, “stack underflow!\n”); return 0; } }

double peek() { //lettura non distruttiva dell’ultimo dato if (stack.sindex > 0) seturn stack.s[stack.sindex -1]; else { fprintf(stderr, “stack underflow!\n”); return 0; } }

void dup() { //duplicazione dell’ultimo dato inserito push(peek()); }

void swap() { //inversione degli ultimi due dati immessi double a, b; a = pop(); b = pop(); push(a); push(b); }

void print() { //stampa dell’ultimo dato inserito printf(“%3.2lf\n”, peek()); }

/* Con la presente libreria di funzioni sullo stack si può usare la struttura dati per realizzare una calcolatrice in logica polacca inversa (RPN) */

int parse (char inp) { //interprete di stringhe di cui si serve rpn char x; double val; if ((inp == NULL) || strlen(inp) <1) { print(); return 0; } x = inp[0]; //controlla il primo carattere della stringa switch(x) { //serie di operazioni case (‘+’): push(pop()+pop()); print(); break; case (‘-‘): push(pop()-pop()); print(); break; case (‘’): push(pop()*pop()); print(); break; case (‘/’): push(pop()/pop()); print(); break; case (‘^’): push(pow(pop(), pop())); print(); break; case (‘s’): push(sin(pop())); print(); break; case (‘ ‘): print(); break; case (‘q’): return -1; //chiude la funzione default: val = atof(inp); push(val); //inserisce numeri nello stack } return 0; }

void rpn() { //sarà l’unica funzione da chiamare nel main char line[512]; int chk; //conterrà il valore di controllo della parse do { putchar(‘>’); //crea un prompt fflush(stdout); //invia ttti gli output a schermo gets(line); chk = parse(line); //manda la linea immessa all’interprete } while (chk==0); //esce quando è stata immessa una q; }

/* Si può fare una versione migliorata delle funzioni di libreria dello stack, che invece di stampare messaggi al loro interno restituiscono le condizioni indesiderate come int */

int push(double val) { if (stack.sindex < stack.size) { stack.s[stack.sindex] = val; stack.sindex++; return 0; //funzione andata a buon fine } else return -1; //funzione non andata a buon fine }

int peek(double* val) { //dato che restituiamo un int, per potere tirare //fuori dallo stack un valore dobbiamo metterlo //in un puntatore passato by reference if (stack.sindex > 0) { *val = stack.s[stack.sindex-1]; return 0; //funzione andata a buon fine } else return -1; //funzione non andata a buon fine }

int pop(double* val) { if (stack.sindex > 0) { *val = stack.s[--stack.sindex]; return 0; //funzione andata a buon fine } else return -1; //funzione non andata a buon fine }

LISTE

Lista LIFO

/* Una lista è una struttura dati dinamica, cioè che può essere espansa e rimpicciolita a runtime. È costituita da nodi. Una prima opportunità data dalla lista è quella di rappresentare uno stack, ovvero fare in modo che l’inserimento e l’estrazione dalla lista seguano uno schema di tipo LIFO (last in first out)

La lista lifo ha complessità computazionale di inserimento pari a 1 (dato che bisogna solamente allocare un nodo e inserire in testa), stesso per l’estrazione. Per quanto riguarda la ricerca nella lista la complessità computazionale è lineare perché non essendoci un ordinamento si devono scorrere tutti gli elementi */

typedef struct node { double val; //valore contenuto nel nodo della lista struct node* next; //puntatore al nodo successivo; } Nodo;

Nodo* push(double val, Nodo* lista) { //aggiunge un elemento in testa //nel main chiameremo la funzione nel modo //seguente: lista = push(val, lista); Nodo* first; first = (Nodo*) malloc(sizeof(Nodo)); //alloca il nuovo punto d’accesso first->val = val; first->next = lista; //istruzione valida anche se lista=NULL return first; }

Nodo* pop(Nodo* lista, double* val) { //recupera un elemento dalla lista //da chiamare con: lista = pop(lista, &x) Nodo* tmp; //puntatore d’appoggio per la free if (lista == NULL) { //caso lista vuota fprintf(stderr, “Stack empty!\n”); return NULL; } *val = lista->val; //passa by reference il valore contenuto tmp = lista->next; //fa scorrere tmp all’elemento successivo free(lista); //elimina il nodo relativo all’el. Estratto return tmp; //setta tmp come punto d’accesso }

double peek(Nodo* lista) { //peek non prevede riorganizzazioni //quindi può restituire un double if (lista == NULL) { printf(“stack underflow!\n”); return -1; //restituisce un valore supposto non valido } Else return lista->val; //restituisce il valore senza cancellarlo }

void printall(Nodo* lista) { //stampa tutti gli elementi della lista if (lista == NULL) return; //non ha nulla da stampare printf(“%3.2lf\n”, lista->val); printall(lista->next); //chiama ricorsivamente se stessa }

void printallR(Nodo* lista) { //stampa tutti gli elementi della lista if (lista == NULL) return; //non ha nulla da stampare printallR(lista->next); //chiama ricorsivamente se stessa printf(“%3.2lf\n”, lista->val); }

void tovett(Nodo* lista, double* vett) { //mette gli elementi in un vettore int i=0;

Nodo* tmp = lista; //puntatore per scorrere nella lista while (tmp != NULL) { vett[i] = tmp->val; tmp = tmp->next; //avanza nella lista i++; } }

Nodo* deletefirst(Nodo* lista) { //elimina il primo elemento in una lista Nodo* tmp; tmp = lista->next; free(lista); return tmp; }

Nodo* deleteallvalues(Nodo* lista, double val) { //elimina tutte le occorrenze di val Nodo* r, *s; if (lista != NULL) { //esegue operazioni solo se ci sono elementi if (lista->next != NULL) { //se la lista è costituita da >1 elementi s = lista; //usa s per modificare la lista while (s->next != NULL) { if (s->next->val == val) { //eliminazione dell’elemento r = s->next; s->next = s->next->next; free(r); } else s=s->next; //va avanti } } if (lista->val == val) { //caso in cui l’elemento cercato è il primo r = lista->next; free(lista); //elimina la lista return r; //il 2o elemento diventa punto d’accesso } return lista; //se non ha cancellato il primo ritorna l’accesso }

int size(Nodo* lista) { //numero di elementi della lista if (lista == NULL) return 0; else return 1 + size(lista->next); }

/* si possono migliorare le funzioni che gestiscono la lista, facendo in modo che i punti d’accesso vengano aggiornati non con i valori di ritorno ma con passaggi di puntatori by reference */

void push(double val, Nodo** lista) { Nodo* first; first = (Nodo) malloc(sizeof(Nodo)); first->val = val; first->next = (lista); (*lista) = first; //aggiorna la zona di memoria puntata }

double pop1(Nodo** lista) { //versione 1 della pop, che ritorna il valore Nodo* tmp; double val; if ((lista) == NULL) { printf(“stack vuoto!\n”); return -1; } val = (lista)->val; tmp = (lista)->next; free(lista); //eliminazione del nodo relativo al valore (*lista) = tmp;

Lista doppiamente linkata

/* lista doppiamente linkata: prevede la presenza di un puntatore prev e un puntatore next in ogni nodo */

typedef struct node { double val; struct node* next; struct node* prev; } Nodo; void push(Nodo** lista, Nodo** fine, double val) { Nodo* first; first = (Nodo) malloc(sizeof(Nodo)); first->val = val; if ((lista) == NULL){ //caso lista vuota (fine) = first; //setta al nuovo nodo entrambi i punti d’accesso (lista) = first; (lista)->prev = NULL; //esistendo solo lista, non ci sono prev e next (lista)->next = NULL; return; } first->next = (lista); //caso generico: setta il nuovo next (lista)->prev = first; //setta il puntatore del precedente punto d’acc (*lista) = first; //setta il nuovo punto d’accesso }

int pop(Nodo** lista, double* val) { //pop con flag Nodo* tmp; if ((lista) == NULL) { //caso lista vuota return -1; } val = (lista) -> val; //restituzione del valore by reference tmp = (lista) -> next; //spostamento dell’accesso free(lista); (lista) = tmp; (lista)->prev = NULL; //aggiornamento puntatore del punto d’acc Return 0; //funzione andata a buon fine }

void printall(Nodo* iniziolista) { //stampa tutti dall’inizio Nodo* tmp; tmp = iniziolista; while (tmp != NULL) { printf(“%3.2lf\n”, tmp->val); tmp = tmp->next; } }

void printallR(Nodo* finelista) { //stampa tutti dalla fine Nodo* tmp; tmp = finelista; while (tmp != NULL) { printf(“%3.2lf\n”, tmp-val); tmp = tmp->prev; } }

Lista circolare

/* Ultimo tipo di liste: liste circolari, in cui a meno che la lista non sia vuota non ci sono puntatori a NULL. Nelle liste circolari non c’è un punto d’accesso privilegiato (inizio o fine) ma un punto d’accesso temporaneo (corrente) che può essere spostato usando delle funzioni. */

typedef struct node { double val; struct node* next;

struct node* prev; } Node;

Nodo* push(Nodo* current, double v) { //inserisce un valore prima del corrente //e lo fa diventare il nuovo corrente if (current == NULL) { //caso 1: la lista è vuota Nodo* tmp = NULL; tmp = (Nodo) malloc(sizeof(Nodo)); tmp->val = v; tmp->next = tmp; //essendo l’unico, punta a se stesso tmp->prev = tmp; return tmp; } if (current->next == current) { //caso 2: un solo elemento Nodo tmp = NULL; tmp = (Nodo) malloc(sizeof(Nodo)); tmp->val=v; tmp->next = current; //il next punta al precedente “corrente” tmp->prev = current; //il prev punta al precedente “corrente” current->next = tmp; //analogamente si sistema il vecchio “corrente” current->prev = tmp; return tmp; //tmp sarà restituito come nuovo punto d’accesso } Nodo tmp = NULL; //gestisce i casi di lista con >=2 elementi tmp = (Nodo) malloc(sizeof(Nodo)); tmp->val = v; Nodo newprev = current->next; //crea un nuovo nodo d’appoggio while (newprev->next != current) newprev = newprev->next; //scorre la lista fino a quando non trova //il nodo precedente al vecchio “current” newprev->next = tmp; tmp->prev = newprev; //sistema i puntatori precedenti al nuovo current tmp->next = current; current->prev = tmp; //sistema i puntatori dopo il nuovo current return tmp; //tmp è il nuovo current }

void printall(Nodo* current) { //stampa tutto dal corrente in ordine di next Nodo* tmp = current; do { printf("%3.2lf\n", tmp->val); tmp = tmp->next; } while (tmp != current); return; }

void printallreverse(Nodo* current) { //stampa tutto dal corrente in ordine di prev Nodo* tmp = current; do { printf("%3.2lf\n", tmp->val); tmp = tmp->prev; } while (tmp != current); return; }

void printcurrent(Nodo* current) { //stampa il valore del punto d’accesso corrente printf("%3.2lf\n", current->val); return; }

Nodo* gonext(Nodo* current) { //sposta avanti il punto d’accesso corrente if (current == NULL) return NULL; else return current -> next; }

Nodo* goprev(Nodo* current) { //sposta indietro il punto d’accesso corrente if (current == NULL) return NULL;