Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
*----------------------------------------------------*
PROGETTO DI PROGRAMMAZIONE II
E LABORATORIO DI
PROGRAMMAZIONE II
LIPARDI PIERLUIGI
0124000867
Esercizi svolti: Sistema operativo: Windows 8.1 a 64 bit
Livello 1 : 36/36 Compilatore: Dev-C++ versione 5.6.2
Livello 2 : 2/19 Pacchetto utilizzato: Microsoft Office 2007
Livello 3 : 1/20 Altri programmi : Paint e Adobe Reader
Gli esercizi sono stati commentati per permettere al lettore una migliore
comprensione sia dei passaggi svolti sia della logica che c’è dietro ogni
programma nella relazione. C’è anche un sommario per arrivare facilmente
agli esercizi desiderati.
*----------------------------------------------------*
-2-
Indice:
ESERCIZI LIVELLO 1
Argomento 1: Operatori sui singoli bit, bitwise operators
01……………………………………………………………………………………………………pag 4
02……………………………………………………………………………………………………pag 4-5
03……………………………………………………………………………………………………pag 5-7
04……………………………………………………………………………………………………pag 8-10
Argomento 4: Le stringhe
22(Quiz)……………………………………………………………………………………………..pag 27
23…………………………………………………………………………………………………….pag 27-28
24…………………………………………………………………………………………………….pag 29-30
25…………………………………………………………………………………………………….pag 31
26_parte1…………………………………………………………………………………………….pag 32
Argomento 9: Strutture dati dinamiche gerarchiche alberi, alberi binari, alberi di ricerca, heap
49……………………………………………………………………………………………………..pag 61-62
ESERCIZI LIVELLO 2
Argomento 3: Sistemi aritmetici di un computer, tipo numerico reale floating-point
17……………………………………………………………………………………………………pag 79-80
ESERCIZI LIVELLO 3
Argomento 12: Algoritmi di ordinamento Divide et Impera: Quick-Sort, Merge-Sort, Heap-Sort
73……………………………………………………………………………………………………pag 83-84
-4-
/*ESERCIZIO 1: Scrivere una function C char low_upp(char ch) che cambia il carattere
in input da minuscolo a maiuscolo e viceversa automaticamente */
#include <stdio.h>
char low_upp(char ch);
void main()
{
char ch;
printf("Puoi scegliere di inserire il carattere o minuscolo o maiuscolo.\n");
printf("\nInserisci qui il carattere: ");
scanf("%c",&ch);
ch=low_upp(ch);
printf("\nIl carattere convertito e' questo: %c",ch);
}
/*La function prende in input un carattere e restituisce un dato dello stesso tipo.
La relazione tra un carattere maiuscolo e uno minuscolo riguarda solamente un bit: il
sesto bit contando da destra verso sinistra la stringa di 8 bit relativa al carattere. Si puo' agire
sul sesto bit della stringa mediante l'operatore bitwise XOR(^) che si comporta in questo modo: se i
due bit operandi sono uguali il risultato e’ 0, invece se i due bit operandi sono diversi il risultato
e' 1.
Ad esempio:
A= 65 ossia 01#0#0 0001 in binario
a= 97 ossia 01#1#0 0001 in binario*/
char low_upp(char ch)
{
char carattere;
carattere=ch^32;
return carattere;
}
Screenshot esercizio 1
/*ESERCIZIO 2: Scrivere una function C char rotate(char ch, char n_bit) per ruotare di n bit (n_bit),
verso sinistra o verso destra (rispettivamente per n_bit<0 e per n_bit>0), il contenuto di una
variabile char mediante gli operatori bitwise*/
#include<stdio.h>
char rotate(char ch, char n_bit);
void main()
{
unsigned char ch;/*unsigned perche' se no dopo la rotazione esce un valore con il segno*/
int n_bit;/*il numero di bit da shiftare*/
printf("Inserisci qui il carattere (a...z oppure A...Z): ");
scanf("%c",&ch);
printf("Il valore intero del carattere nel codice ASCII vale: %d",ch);
printf("\n\nDi quanti bit vuoi fare lo shift?");
printf("\nMetti un numero negativo per shiftare a sinistra");
printf("\nMetti un numero positivo per shiftare a destra");
printf("\nNumero di bit inserito: ");
scanf("%d",&n_bit);
ch=rotate(ch,n_bit);
printf("\nEcco il valore dopo lo shift: %d",ch);
}
-5-
if(n_bit>0)/*rotazione a destra*/
{
ch=ch>>n_bit;/*questo e' lo shift principale*/
temp=temp<<8-n_bit;/*eseguo il salvataggio dei bit che verrebbero persi nello shift*/
return ch|temp;/*uniamo le due porzioni attraverso l'operatore bitwise or(inclusivo)*/
}
else if(n_bit<0)/*rotazione a sinistra*/
{
n_bit=-n_bit;/*cambiamo il segno di n_bit in positivo perche' a noi serve il modulo*/
ch=ch<<n_bit;/*questo e' lo shift principale*/
temp=temp>>(8-n_bit);/*eseguo il salvataggio dei bit che verrebbero persi nello shift*/
return ch|temp;/*UNIAMO le due porzioni attraverso l'operatore bitwise or(inclusivo)*/
}
}
Screenshot esercizio 2
/*ESERCIZIO 3
Scrivere una function C che, dopo aver estratto i bit da una variabile intera X (tipo char, short o
int) ne calcola il valore corrispondente dalla formula:
Val_X = b[n-1] * 2^(n-1) + ... + b[2] * 2^2 + b[1] * 2^1 + b[0] * 2^0
dove b e' l'array dei bit di X:b[j], per j=0,1....,n-1 dal meno significativo al piu' significativo,
(dove n=8 per il tipo char, n=16 per il tipo short o n=32 per il tipo int).
Confrontare il risultato con il valore immesso per la variabile intera X dichiarata una volta signed ed
un'altra unsigned.*/
#include<stdio.h>
#include<math.h>/* mi serve per usare la potenza pow(x,y)*/
#include<stdlib.h> /*questa libreria mi serve per usare exit()*/
#define MAX_LEN 32 /*questo e' il numero massimo di bit di intero long*/
void bit_show(short len, char ch[], short bit[]);
void visualizza_dec(short len, short bit[]);
void main()
{
short menu, bit[MAX_LEN], k;
unsigned char len;/*len mi rappresenta la lunghezza della variabile in esame*/
/* COSTRUTTO UNION
Occorre una function che visualizzi la rappresentazione binaria di un numero di tipo qualunque.
Per realizzare una function del genere occorre usare il costrutto UNION in cui
le variabili possono essere considerate viste diverse di una stessa area di memoria.
La dichiarazione risulta simile a quella delle struct in C.
La differenza sostanziale tra i due costrutti riguarda come sono allocati i byte della memoria
per ciascun tipo di dato. In particolare:
- nella struct ogni campo ha un suo indirizzo
- nella union tutti i campi partono tutti dallo stesso indirizzo(si sovrappongono)
-6-
D'altra parte, visto che la UNION usa lo stesso numero di byte per le varie variabili, avremo
che:
- la variabile L (array di 1 long) utilizzera' 32 bit (4 byte)
- la variabile S (array di 2 short) utilizzera' 2x16 bit = 32 bit
- la variabile C (array di 4 char) utilizzera' 4x8bit = 32 bit
Le 3 variabili condividono la stessa area di memoria e, quindi, fanno riferimento alle stesse
locazioni di memoria */
union word32bit
{
long L[1];
short S[2];
char C[4];
}word;
puts("\n\nFa la tua scelta");
puts("\n[0] Esci");
puts("\n[1] Intero char in binario");
puts("\n[2] Intero short in binario");
puts("\n[3] Intero long in binario\n");
do{
printf("\n\nScelta: ");
fflush(stdin);
scanf("%hd", &menu);
switch(menu)
{
case 0 : exit(0);
case 1 : /*visualizzo i bit corrispondenti a una variabile di tipo char*/
len=sizeof(char);
printf("Immettere un intero char -> ");
fflush(stdin);
scanf("%d", &(word.C[0]));/*fa rifermento alla prima componente*/
puts("Char in decimale e esadecimale");
printf("C=%+10hd, hex=%02x",word.C[0],word.C[0]);
bit_show(sizeof(char),word.C,bit);/*estraggo i bit*/
visualizza_dec_bin(sizeof(char), bit);
break;
case 2 : /*visualizzo i bit corrispondenti a una variabile di tipo short*/
len=sizeof(short);
printf("Immettere un intero short -> ");
fflush(stdin);
scanf("%hd", &(word.S[0]));/*fa riferimento alla prima componente*/
puts("Short in decimale e esadecimale");
printf("S=%+10hd, hex = %04x; ", word.S[0],word.S[0]);
bit_show(sizeof(short),word.S, bit);/*estraggo i bit*/
visualizza_dec_bin(sizeof(short), bit);
break;
case 3 : /*visualizzo i bit corrispondenti a una variabile di tipo long int */
len=sizeof(long);
printf("Immettere un intero long -> ");
fflush(stdin);
scanf("%ld", &(word.L[0]));/*fa riferimento alla prima componente*/
puts("Long in decimale e esadecimale");
printf("L=%+10d, hex =%08lx; ", word.L[0],word.L[0]);
bit_show(sizeof(long),word.L,bit);/*estraggo i bit*/
visualizza_dec_bin(sizeof(long), bit);
break;
default : exit(1);
}
for(k = 8*len-1; k>=0; k--)/*stampo i bit*/
{
(k%4 == 0) ? printf("%1d ",bit[k]) : printf("%1d", bit[k]);
}
}while(menu!=0);
}
-7-
/*ESERCIZIO 4
Scrivere una function C per estrarre dalla variabile intera X i k bit più significativi o meno
significativi,
dove X e k sono i parametri di input, usando:
1) Una maschera.
2) L’operatore di shift (>> o <<).
3) Il prodotto o la divisione per potenze di 2 */
#include <stdio.h>
#include<math.h>
#define MAX_LEN 8 /*questa e' la lunghezza massima della stringa in bit*/
/*la scelta di unsigned e' perche (2^8-1) mi darebbe un valore negativo,
quindi consideriamo il valore x in modulo affinche' cio' non accadi*/
unsigned char shift(unsigned char x,short k,short bit[]);
unsigned char maschera(unsigned char x,short k);
unsigned char prod_div(unsigned char x,short k,short bit[]);
void bit_show(short len,unsigned char x,short bit[]);
void stampa_bit(short bit[]);
void main()
{
unsigned char ris_shift, ris_prodiv;
unsigned char mask;
short bit[MAX_LEN];
short len;
unsigned char x;
short k;
printf("Inserisci il valore della variabile X di tipo intero: ");
scanf("%d",&x);
printf("\nIl valore iniziale della variabile e': ");
bit_show(sizeof(short),x,bit);
stampa_bit(bit);
printf("\n");
/*adesso decidiamo se estrarre i bit piu' significativi o meno significativi,
inserendo un numero in input*/
printf("\nQuanti bit vuoi estrarre?\nInserisci un valore negativo per i bit meno
significativi\noppure un valore positivo per i bit piu' significativi\n");
printf("\nEcco il numero di bit che hai scelto: ");
scanf("%hd",&k);
fflush(stdin);
mask=maschera(x,k);/*ecco la chiamata alla funzione relativa alla maschera*/
printf("\nBIT ESTRATTI CON LA MASCHERA: ");
bit_show(sizeof(short),mask,bit);
stampa_bit(bit);
ris_shift=shift(x,k,bit);/*ecco la chiamata alla funzione relativa allo shift*/
printf("\nBIT ESTRATTI CON LO SHIFT: ");
bit_show(sizeof(short),ris_shift,bit);
stampa_bit(bit);
ris_prodiv =prod_div(x,k,bit);/*ecco la chiamata alla funzione relativa al prodotto-divisione*/
printf("\nBIT ESTRATTI CON IL PRODOTTO: ");
bit_show(sizeof(short),ris_prodiv,bit);
stampa_bit(bit);
}
}
else if(k<0)
{
k=-k;/*cambiamo segno a k perche' ci serve il modulo*/
x =x<<(MAX_LEN - k);/*shifto a sinistra per eliminare i bit che non mi servono*/
x =x>>(MAX_LEN - k);/*shifto a destra per riportare alla posizione iniziale i bit
meno significativi che voglio estrarre*/
}
return x;
}
}
Screenshot esercizio 4
/*ESERCIZIO 5
Scrivere due function C di conversione di un intero positivo (int)
da base 10 a base 2 mediante l’algoritmo delle divisioni successive realizzato rispettivamente:
• Usando gli operatori di quoziente e resto della divisione intera;
• Usando gli operatori bitwise */
/*Ricordiamo questa cosa: a = c : b
Nell'espressione sopra, a rappresenta il quoziente (quoto nel caso di divisione senza resto),
b il divisore (cioè la quantità che divide) e c il dividendo (cioè la quantità da dividere) */
#include<stdio.h>
#define MAX_LEN 16/*questo e' il numero massimo di bit di intero short*/
void conv_quozerest(unsigned short n,unsigned short bit[]);
void conv_bitwise(unsigned short n, unsigned short bit[]);
void stampa(short bit[]);
void main()
{
unsigned short n;
unsigned short bit[MAX_LEN];/*questa variabile ci serve per la visualizzazione binaria*/
printf("Inserisci un numero INTERO POSITIVO: ");
scanf("%hd",&n);
printf("\nConversione con operatori di quoziente e resto:\n");
conv_quozerest(n,bit);
stampa(bit);
printf("\n\nConversione con operatori bitwise:\n");
conv_bitwise(n,bit);
stampa(bit);
}
void stampa(short bit[])/*questa funzione mi stampa i bit nell'ordine dal piu' significativo
al meno significativo nel senso che bit[0]=LSB-LEAST SIGNIFICANT BIT*/
{
short i;
for(i=MAX_LEN-1;i>=0;i--)
{
(i%4 == 0) ? printf("%1d ",bit[i]) : printf("%1d", bit[i]);
}
}
Screenshot esercizio 5
/*ESERCIZIO 6
Scrivere una function C di conversione di un intero positivo da base 2 a base 10,
mediante l’algoritmo delle divisioni successive, che generi un array di caratteri
contenenti le cifre decimali.*/
#include <stdio.h>
#include<math.h>
#define MAX_LEN 8
#define LEN 3/*la massima lunghezza dell'array di caratteri deve essere 3 perche' possono essere
inseriti fino a 256 valori, incluso lo 0*/
void conv_base10tobase2(unsigned short n,unsigned short bit[]);
unsigned short conv_base2tobase10(unsigned short bit[]);
void array(unsigned short n, unsigned char cifre_decimali[]);
void stampa_bit(short bit[]);
void main()
{
short i,cifre;
unsigned short n;
unsigned short bit[MAX_LEN];
unsigned char cifre_decimali[LEN];/*questo array di caratteri mi conterra' le cifre decimali*/
/*Dato che l'intervallo di rappresentazione del tipo char e' [-128,+127] i numeri che vanno
presi in considerazione sono 256 includendo lo zero*/
// su 8 bit si rappresentano ((2^8)-1) valori dalla formula (2^n)-1 dove n e’ il numero di bit
printf("Inserisci un numero INTERO POSITIVO (scegli da [0,255] estremi compresi): ");
scanf("%hd",&n);
printf("\nEcco il numero convertito da base 10 a base 2: ");
conv_base10tobase2(n,bit);//chiamata alla funzione per convertire da base 10 a base 2
stampa_bit(bit);
n=conv_base2tobase10(bit);//chiamata alla funzione per convertire da base 2 a 10
printf("\n\nEcco il numero riconvertito da base 2 a base 10: %d",n);
array(n,cifre_decimali);
}
- 12 -
/*ESERCIZIO 8
Scrivere una function C per eseguire l'addizione aritmetica binaria di due numeri
naturali p e q (p,q•appartenenti ad N) mediante gli operatori bitwise
(come da algoritmo in P-like in esercizio 11) , traducendo l'algoritmo di seguito riportato:
{Algoritmo di addizione binaria mediante operatori sui bit}
procedure binary_add(op1,op2)
rip:=1;
while rip>0
sum:=bitXOR(op1,op2);
rip:=bitAND(op1,op2);
rip:=leftSHIFT(rip,1);
op1:=sum; op2:=rip;
endwhile */
#include<stdio.h>
short addizione(short p,short q);
void main()
{
short p,q;
printf("Inserisci il primo numero di tipo intero (p): ");
scanf("%hd",&p);
printf("\nInserisci il secondo numero di tipo intero (q): ");
scanf("%hd",&q);
/*E' ovvio che se il risultato sarà negativo, si e' verificato il fenomeno dell'overflow che
si presenta quando si tenta di scrivere un numero piu’ grande del massimo consentito dalla
configurazione di n_bit: nel nostro caso il range dello short e’ [0,+32767]*/
printf("\nL'addizione aritmetica vale: %hd",addizione(p,q));
}
Screenshot esercizio 8
- 14 -
/*ESERCIZIO 9:
Scrivere una function C per eseguire la sottrazione aritmetica* binaria
(*: cioe' primo operando maggiore del secondo) di due numeri naturali
p e q (p, q, p-q•appartenenti a N) mediante gli operatori bitwise*/
#include<stdio.h>
int sottrazione(short p, short q);
void main()
{
short p,q;
printf("Inserisci il primo numero intero (p): ");
scanf("%hd",&p);
printf("\nInserisci il secondo numero intero (q): ");
/*Visto che facciamo la sottrazione, il primo operando deve essere maggiore del secondo*/
do
{
scanf("%hd",&q);
if(q>p)
{
printf("\nHai sbagliato reinserisci il numero intero q
(deve essere minore di p):\n");
}
}while(q>p);
printf("\nIl risultato della sottrazione e' (%hd)-(%hd): %hd",p,q,sottrazione(p,q));
Screenshot esercizio 9
- 15 -
/*ESERCIZIO 10:
Scrivere una function C che, fissato il numero n di bit, calcoli la rappresentazione di un intero:
1)per complemento a 2;
2)eccesso B (B-biased) */
#include<stdio.h>
#include<math.h>
#define MAX_LEN 8
void stampa_bit(int bit[]);
void conv_quozerest(int n,int bit[]);
int complemento(int n);
int eccesso(int n);
void main()
{
int risult_compl,risult_ecc;
int i_min_compl,i_max_compl;
int i_min_ecc,i_max_ecc;
int bit[MAX_LEN];
int n;
/*Ricordiamo che il complemento a 2 si usa per memorizzare i numeri interi con segno:
ovviamente c'e' un intervallo non simmetrico di rappresentazione oltre il quale ci sono
problemi,arrecando risultati sbagliati*/
printf("\nIl range per complemento a 2 e' il seguente:\n ");
i_min_compl=-(pow(2,MAX_LEN-1));/*questo e' il valore minimo del range complemento: -2^(n-1)*/
i_max_compl=pow(2,MAX_LEN-1)-1;/*questo e' il valore massimo del range complemento: 2^(n-1)-1*/
printf("[%d][%d]",i_min_compl,i_max_compl);
/*Ricordiamo che la rappresentazione biased si usa per il campo esponente di un numero reale
floating point: ovviamente c'e' un intervallo non simmetrico di rappresentazione oltre il quale
ci sono problemi, arrecando risultati sbagliati*/
printf("\nIl range per eccesso biased e' il seguente:\n ");
i_min_ecc=-(pow(2,MAX_LEN-1)-1);/*il valore minimo del range eccesso B: -(2^(n-1)-1)) */
i_max_ecc=pow(2,MAX_LEN-1);/*questo e' il valore massimo del range eccesso B: 2^(n-1)*/
printf("[%d][%d]",i_min_ecc,i_max_ecc);
printf("\n\nInserisci il numero di cui vuoi fare la rappresentazione: ");
scanf("%d",&n);
printf("\nRappresentazione complemento a 2\n");/*Il complemento a 2 si comporta cosi':se il
numero inserito e' positivo allora il complemento coincide con il numero iniziale. Se il numero
inserito e' negativo allorail complemento vale (2^MAX_LEN-n) dove MAX_LEN indica il numero di
bit e n il numero inserito*/
/*qui facciamo il controllo se il numero inserito va al di fuori dell'intervallo*/
if(n>=i_min_compl&&n<=i_max_compl)
{
risult_compl=complemento(n);
conv_quozerest(risult_compl,bit);
stampa_bit(bit);
}
else
{
printf("Valore non consentito poiche' va oltre l'intervallo sopra mostrato");
}
/*ancora verifichiamo se andiamo al di fuori del range con il numero inserito*/
printf("\nRappresentazione biased\n");
if(n>=i_min_ecc&&n<=i_max_ecc)
{
risult_ecc=eccesso(n);
conv_quozerest(risult_ecc,bit);
stampa_bit(bit);
}
else
{
printf("Valore non consentito poiche' va oltre l'intervallo sopra mostrato");
}
}
- 16 -
int complemento(int n)/*Annotando che MAX_LEN e' il numero di bit e n il valore intero inserito,
usiamo la formula matematica per calcolare la rappresentazione
biased: essa e'[2^MAX_LEN+n]%2^MAX_LEN. Si potrebbe anche fare il complemento
a uno con l'operatore NOT bit a bit e poi sommare a 1 con resto
di 2^MAX_LEN*/
{
int potenza=pow(2,MAX_LEN);
n=(potenza+n)%potenza;
return n;
}
int eccesso(int n)/*Annotando che n e' il numero intero inserito, B il BIAS e MAX_LEN il numero di bit,
la rappresentazione biased di un intero si ottiene sommando il valore del BIAS
all'intero, ricordando che il BIAS vale (2^(MAX_LEN-1)-1), e quindi otteniamo:
n+B=n+(2^(MAX_LEN-1)-1)*/
{
int B;
B=pow(2,MAX_LEN-1)-1;
n=n+B;
return n;
}
Screenshot esercizio 10
- 17 -
/*ESERCIZIO 11:
Conoscendo la rappresentazione degli interi in C, riscrivere la function C per
l'addizione binaria di due interi (Z) mediante gli operatori bitwise,
traducendo l'algoritmo di seguito riportato:
*Algoritmo di "addizione binaria" mediante operatori sui bit
rip:=1;
while rip>0
sum := bitXOR(op1,op2);
rip := bitAND(op1,op2);
rip := leftSHIFT(rip,1);
op1 := sum; op2:=rip;
endwhile
Se l’operazione da implementare deve essere l’addizione algebrica (cioè deve valere anche per
gli interi negativi rappresentati per complemento a 2), quale accorgimento va usato nella
traduzione in C dell’algoritmo ... e perché.*/
#include<stdio.h>
short addizione(short p,short q);
void main()
{
short p,q,risultato;
printf("Inserisci qui il primo operando(scegli se neg o pos): ");
scanf("%hd",&p);
printf("\nInserisci qui il secondo operando(scegli se neg o pos): ");
scanf("%hd",&q);
risultato=addizione(p,q);
printf("\nIl risultato dell'addizione tra (%hd)+(%hd) e': %hd",p,q,risultato);
/*ESERCIZIO 12:
Scrivere una function C per visualizzare la rappresentazione binaria (s,e,m) di un numero float.
Verificare che il valore del numero ottenuto coincida con il dato iniziale */
#include<stdio.h>
#include<math.h>
#define BIAS 127/*127 e' il valore del BIAS in singola precisione*/
#define MAX_LEN 32 /*consideriamo la rappresentazione in singola precisione(32 bit) del float secondo
lo standard IEEE 754*/
void visualizza_bit(char bit[]);
float estrae_bit(int n,char bit[]);
void main()
{
char bit[MAX_LEN];
float verifica;
/*si usa la union sp(singola precisione) che contiene due variabili di tipo diverso
che condividono la stessa area di memoria: quando ci servira' di usare
la variabile float, nella lettura del valore reale da inserire, useremo il campo fn;
quando nella la funzione estrae(n,bit) dobbiamo usare i bitwise dobbiamo per forza usare
il campo in di tipo intero perche' solo con le varibili intere si possono usare i bitwise*/
union sp
{
float fn;
int in;
}n;
printf("Inserisci un numero di tipo reale(float): ");
scanf("%f",&n.fn);
verifica=estrae_bit(n.in,bit);
printf("\nBit corrispondenti SEGNO ESPONENTE MANTISSA");
visualizza_bit(bit);
printf("\n\nEcco la verifica: %f",verifica);
}
Screenshot esercizio 12
/*ESERCIZIO 14:
Scrivere delle function C per calcolare rispettivamente l’epsilon macchina del tipo float,
del tipo double e del tipo long double, visualizzando ad ogni passo i singoli bit.
Confrontare i risultati ottenuti con i valori delle variabili predefinite FLT_EPSILON,
DBL_EPSILON e LDBL_EPSILON.*/
#include<stdio.h>
#include<float.h>/*float.h ci consente di visualizzare le variabili predefinite FLT_EPSILON,
DBL_EPSILON e LDBL_EPSILON*/
#define MAX_LEN 80/*la massima lunghezza dell'epsilon macchina considerando la lunghezza long_double*/
void estrae_bit(short len, char ch[], char bit[]);
void stampa_bit_float(char bit[]);
void stampa_bit_double(char bit[]);
void stampa_bit_long_double(char bit[]);
void main()
{
short k;
char bit[MAX_LEN];
float epsilon_f;/*questo e' l'epsilon di tipo float*/
float epsilon1_f;/*questa e' la variabile di appoggio relativa all'epsilon di tipo float*/
/*L'uso del costrutto union e' dovuto al fatto che devo usare variabili intere
per la rappresentazione in binario*/
union sp/*UNION RELATIVA ALLA SINGOLA PRECISIONE*/
{
float f;
int n_f[1];
char c[4];
}x;
z.ld=epsilon1_ld;/*nel campo long double della union ldb e' contenuta la variabile di appoggio
in precisione long double*/
printf("PRECISIONE SINGOLA\n");
printf("Float x = %f\n",x.f);/*stampiamo il valore dell'epsilon macchina in singola precisione,
che in questo caso viene approssimato a 2*/
/*cicliamo per 23 volte perche' 23 e' il numero di bit della mantissa realmente rappresentati
in singola precisione*/
for(k=0;k<24;k++)
{
printf("\nn = %hd",k);
estrae_bit(sizeof(x.f),x.c,bit);
stampa_bit_float(bit);
printf("\nDOPPIA PRECISIONE\n");
printf("Double y = %f\n",y.d);/*stampiamo il valore di epsilon in doppia precisione che in
questo caso viene approssimato a 2*/
- 21 -
/*cicliamo per 63 volte perche' 63 e' il numero di bit della mantissa realmente
rappresentati in precisione long double*/
for(k=0;k<64;k++)
{
printf("\nn = %hd",k);
estrae_bit(sizeof(z.ld),z.c,bit);
stampa_bit_long_double(bit);
/*L'epsilon macchina si trova effettuando delle divisioni successive per 2, fin quando non
trovo il piu'piccolo numero che sommato ad 1, mi da un valore maggiore di 1*/
epsilon_ld=epsilon_ld/2;
epsilon1_ld=epsilon_ld+1;
z.ld=epsilon1_ld;
}
/*siamo usciti con un primo valore di epsilon che non da contributo quindi dobbiamo tornare
indietro*/
epsilon_d=2.0f*epsilon_d;/*ho trovato l'epsilon, devo ripristinare l'ultima divisione*/
printf("\nVariabile predefinita LDBL_EPSILON \t= %e\n",LDBL_EPSILON);
printf("Variabile calcolata in precisione long double = %e\n",epsilon_ld);
}
/*ESERCIZIO 18
Scrivere una function C per calcolare una somma di molti addendi dello stesso ordine di grandezza. A
scelta la versione
iterativa o ricorsiva dell'algoritmo di somma a blocchi. Applicare l'algoritmo al particolare problema
test di cui e' gia' nota la soluzione.
Ecco il problema:
10^8
---
\
/ 10^-6 ---->soluzione del problema= 100
---
k=1
Ricordiamo che 10^8=100000000 e 10^-6=0,000001*/
#include <stdio.h>
#include <stdlib.h>
#include<math.h>
float somma_blocchi(unsigned int N, float A[]);
void main()
{
float somma;/*la variabile somma e' quella in cui viene collocato il risultato*/
unsigned int k;/*k e' un contatore che ci serve per fare delle iterazioni*/
unsigned int n=pow(10,8);/*n e' il numero di somme da effettuare*/
float *addendi;/*variabile vettore in cui vengono collocati gli addendi da sommare*/
float num=pow(10,-6); /*numero reale da sommare*/
addendi=(float*)malloc(sizeof(float)*n);/*Allochiamo dinamicamente attraverso la funzione
malloc perche' e' richiesto molto spazio. Malloc alloca un blocco di memoria della dimensione
richiesta, in tal caso 10^8, e ne ritorna il puntatore. Se non c'e' memoria disponibile ritorna
NULL. La dimensione massima del blocco di memoria che e' possibile allocare con questa funzione
dipende dal processore, dalla memoria centrale e dal sistema operativo in uso*/
/*l'algoritmo della somma a blocchi si comporta cosi': partendo da un vettore gia' ordinato,
invece di sommare le componenti adiacenti dell'array, somma quelle diametralmente
opposte, cioe' simmetriche rispetto al centro*/
float somma_blocchi(unsigned int N, float A[])
{
unsigned int k,Nmez;
while(N>1)
{
/* Dividi N e fai NMEZ somme. Il primo con l'ultimo, secondo col penultimo... */
Nmez=N/2;
for(k=0;k<Nmez;k++)
{
A[k]=A[k]+A[N-1-k];
}
/* Se N e' dispari allora N=Nmez+1 altrimenti l'elemento di mezzo non considerato tra
le N Dispari somme, si perde.*/
if(N%2 == 0)
{
N=Nmez;
}
else
{
N=Nmez+1;
}
}
- 24 -
/*ESERCIZIO 19:
Scrivere una function C per calcolare iterativamente la somma che segue con il
criterio di arresto naturale.
n
------
\
/ (x^k)/k! che vale circa e^a dove -e- e' il numero di Nepero
------
k=1 */
#include <stdio.h>
#include <float.h>/*mi serve per avere il valore preciso dell'epsilon macchina*/
#include<math.h>/*mi serve per fare l'elevazione a potenza con pow*/
float arresto(float x, float somma,float k);
float fattoriale(float k);
void main()
{
float k=1.0f;
float x,somma=0.0f;
const float e=2.718281828;
printf("FLT_EPSILON vale: %e",FLT_EPSILON);
printf("\nFLT_EPSILON/2 vale: %e",FLT_EPSILON/2);
printf("\nInserire valore A: "); scanf("%f",&x);
somma= arresto(x, somma,k);
printf("\nSomma con criterio di arresto naturale: %e", somma);
printf("\ne^%f vale: %f",x,pow(e,x));
}
/*Il criterio di arresto naturale e' un criterio che ci consente di arrestare la somma
per evitare somme inutili di addendi non significativi rispetto alla somma.
Possiamo avere l'approssimazione di ULP(S)=S*Emach/2 dove ULP indica UNIT IN THE LAST
PLACE di S che si definisce come il minimo numero floating-point positivo che sommato ad S
fornisce un risultato maggiore di S, cioè da contributo ad S*/
float arresto(float x, float somma,float k)
{
float rapporto;
/*Il ciclo while itera fin quando il rapporto: (x^k)/k!
non diventa piu' piccolo di ULP(S) e , quindi, non dara' piu' contributo alla somma*/
while (rapporto>((somma)*FLT_EPSILON*0.5))
{
rapporto=pow(x,k)/(fattoriale(k));
somma = somma + rapporto;/*facciamo la somma incrementale*/
k++;/*incrementiamo il valore di k dopo ogni somma*/
}
return somma;
}
- 25 -
/*questa e' una funzione ricorsiva che mi calcola il fattoriale, che viene calcolato a
sua volta come k=k*(k-1)*(k-2)*... */
float fattoriale(float k)
{
if(k<=1)
{
return 1;
}
else
{
return k*fattoriale(k-1);
}
}
Screenshot esercizio 19
/*ESERCIZIO 20
Scrivere una function C per calcolare la somma di addendi ordinati; sommare gli
addendi una volta in ordine crescente ed un'altra in ordine decrescente.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>// mi serve per definire la costante pi_greco = M_PI = 3.14 e per pow()
#define MAX_DIM 50
float somma_crescente(int n);
float somma_decrescente(int n);
double errore_relativo(double x_appross, float flx);
int main()
{
float Sum_Cre, Sum_Dec;
double x_appross = (M_PI*M_PI)/6; /*La successione e' approssimabile a pi_greco^2/6*/
double Er_rel_cre, Er_rel_dec;
int n;
/*Da questo si evince che la somma crescente e' piu' accurata perche' ha un valore di errore
sicuramente minore rispetto a quello della somma decrescente nel momento in cui si fanno somme
di n numeri dove n si presenta come un valore dal 5000 in poi.
Il problema dell'errore relativo decrescente e' dovuto al fatto che, da una certa iterazione in poi,
l'n-esimo termine della uccessione avra' avuto valore inferiore all'ULP dell'attuale somma parziale,
non dando cosi'alcun contributo al risultato che invece sarebbe dovuto essere diverso. */
printf("\nValore appross(pi^2/6)= %e!\nSum Cre= %e!\nSum Dec= %e!\nErrore relativo cre= %e\nErrore
relativo dec= %e", x_appross,Sum_Cre, Sum_Dec, Er_rel_cre, Er_rel_dec);
return 0;
}
}
Screenshot esercizio 20
- 27 -
/*ESERCIZIO 22 QUIZ: sono stati proposti 2 esercizi dei quali esplicitare il funzionamento e le
differenze principali. Gli esercizi proposti sono i seguenti*/
PRIMO ESERCIZIO
#include<stdio.h>
#include<string.h>
/*Questo programma e' funzionante poiche':
si dichiara una stringa ch che conterra' i singoli caratteri della stringa costante
in questo caso si puo' modificare il puntato della prima cella del vettore*/
void main()
{
char stringa[]="ciao";/*questa e' la dichiarazione di una stringa, cioe' una
sequenza di caratteri*/
puts(stringa);/*puts ci consente di visualizzare su schermo la stringa dichiarata*/
*stringa='m';/*il puntatore stringa contiene il carattere costante 'm', che dopo la
stampa della stringa, va a sostituire il primo carattere della stringa,
eliminando il precedente*/
puts(stringa);/*ristampiamo nuovamente la stringa facendola visualizzare*/
}
Screenshot esercizio 22 quiz-1
SECONDO ESERCIZIO
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
/*la gestione mediante puntatori e' quella ottimale ripetto alla dichiarazione di array
di carattere. Tuttavia in questo caso avviene una segnalazone diagostica:
qui c'e' un puntatore che punta in memoria all'indirizzo base di una stringa costante
che non puo' essere modificata*/
void main()
{
char *stringa="ciao";/*il puntatore stringa contiene la stringa costante ciao*/
puts(stringa);/*stampiamo la stringa per visualizzarla*/
*stringa='m';/*questa e' la parte del codice che mi da errore*/
puts(stringa);/*ristampiamo la stringa*/
}
/*ESERCIZIO 23
Confrontando i risultati con quelli delle relative funzioni del C ed utilizzando per le stringhe:
-l’allocazione statica
-l’allocazione dinamica
scrivere una function C che accetti in input il numero n e legga da tastiera n caratteri (uno alla
volta)costruendo la stringa che li contiene (parametro di output), senza usare strcat(...).*/
/*Ricordiamo che l'allocazione dinamica rispetto all'allocazione statica consente un risparmio di
Memoria, permettendo di allocare o deallocare spazio durante l'esecuzione del programma*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 50 /*questo e' il numero massimo di caratteri inseribili nella stringa*/
void dinamic_string(char *vett,int n);
void static_string(char vettore[],char risultato[],int n);
void main()
{
int n;/*n indica il numero di caratteri da inserire da tastiera*/
int i;
char vettore [MAX], risultato[MAX];/*variabili relativa all'allocazione statica*/
char *vett;/*questa variabile mi serve per l'allocazione dinamica*/
printf("Quanti caratteri vuoi inserire? (max 50): ");
scanf("%d",&n);
static_string(vettore,risultato,n);
dinamic_string(vett,n);
}
- 28 -
/*inseriamo uno per volta i caratteri nella stringa finale per ottenere la concatenazione*/
for(i=0;i<n;i++)
{
risultato[i]=vettore[i];
}
risultato[n]='\0';/*chiudo la stringa*/
printf("\nStringa concatenata: %s",risultato);
}
}
Screenshot esercizio 23
- 29 -
/*ESERCIZIO 24
Confrontando i risultati con quelli delle relative funzioni del C ed utilizzando per le stringhe:
-l’allocazione statica
-l’allocazione dinamica
scrivere una function C che restituisca la concatenazione di due stringhe (parametri di input)
senza usare strcat(...). È a scelta restituire la concatenazione delle due stringhe in una terza
variabile (parametro di output o function stessa) oppure nella prima delle due variabili di input.*/
/*Ricordiamo che l'allocazione dinamica rispetto all'allocazione statica consente un risparmio di
memoria consentendo di allocare o deallocare spazio durante l'esecuzione del programma*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAX 50/*si possono inserire max 50 caratteri in ogni stringa per un totale finale di 100*/
void dinamic_string(char *stringa1, char *stringa2, char *stringa_risult,int n);
void static_string(char stringa_1[], char stringa_2[],char stringa_conc[]);
void main()
{
char stringa_1[MAX],stringa_2[MAX],stringa_conc[2*MAX];/variabili di allocazione statica*/
char *stringa1, *stringa2, *stringa_risult;/*variabili di allocazione dinamica*/
int n;/*questo e' il numero di caratteri che voglio inserire per costruire le due stringhe*/
printf("Inserisci quanti caratteri vuoi mettere(max 50) nelle due stringhe: ");
scanf("%d",&n);
fflush(stdin);/*puliamo il buffer della tastiera per non visualizzare caratteri sporchi*/
static_string(stringa_1,stringa_2,stringa_conc);
dinamic_string(stringa1,stringa2,stringa_risult,n);
/*Inserimento della stringa2 nell'array stringa_conc; i caratteri della stringa_2 vanno inseriti
nell'array, a partire dalla posizione di indice i=n (indice che corrisponde alla posizione
nell'array immediatamente successiva alla stringa_1 già inserita), fino ad i<n+n ovvero
fin quando non ha copiato tutta la seconda stringa avente lunghezza n*/
for (i=n; i<2*n; i++)
{
stringa_conc[i]=stringa_2[j];
j++;/*incrementiamo il contatore j fino a che non sono stati inseriti tutti i caratteri*/
}
stringa_conc[n+n]='\0'; /*chiudiamo la stringa con il backslash 0, il terminatore di fine stringa*/
printf("\nStringa concatenata: %s",stringa_conc);
}
- 30 -
/*ESERCIZIO 25:
Confrontando i risultati con quelli delle relative funzioni del C, scrivere una function C
che restituisca la prima occorrenza di una sottostringa in una stringa senza usare strstr(...).*/
#include <stdio.h>
#include <string.h>
#define MAX_LEN 50
int cerca(char chiave[],char testo[]);
void main()
{
char testo[MAX_LEN];/*questo e' il testo di partenza*/
char chiave[MAX_LEN];/*questo e' il testo da ricercare e ha una massima lunghezza pari
alla lunghezza del testo di partenza*/
int n_occorrenze;
printf("\nInserisci qui il testo di partenza: ");
gets(testo);
printf("\n\t\t\t\t\t 0123456789012345678901234");/*questi numeri servono a riferimento per il
lettore per capire meglio la posizione dei caratteri*/
printf("\nIl testo di partenza inserito e' questo: %s",testo);
printf("\nLa lunghezza del testo e' %d",strlen(testo));
fflush(stdin);
printf("\n\nInserisci qui il testo da ricercare: ");
gets(chiave);
fflush(stdin);
printf("Ecco il testo che hai inserito: %s",chiave);
fflush(stdin);
printf("\nQuesta e' la lunghezza del testo da cercare: %d",strlen(chiave));
n_occorrenze=cerca(chiave,testo);
printf("\n\nLa stringa *%s* appare %d volte nel testo:\n-%s-\n",chiave,n_occorrenze,testo);
}
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void cerca(char *testo, char *patt, int len_t, int len_p);
void main()
{
char *testo;/*questo e' il puntatore alla stringa costante iniziale*/
char *patt;/*questo e' il puntatore alla sottostringa costante da ricercare*/
int len_t, len_p;
testo=(char *)malloc(100);/*allochiamo dinamicamente spazio per inserire il testo principale*/
printf("Immetti il testo principale: ");
gets(testo);
len_t=strlen(testo);/*leggiamo la lunghezza del testo principale senza considerare ‘\0’*/
patt=(char *)malloc(100);/*allochiamo dinamicamente spazio per inserire la sottostringa*/
printf("\nImmetti il pattern da ricercare: ");
gets(patt);
len_p=strlen(patt);/*leggiamo la lunghezza del pattern senza considerareil’\0’*/
printf("\nCerca pattern *%s* di lunghezza %d nel \ntesto principale che ha lunghezza %d ed e'
questo:\n0123456789012345...\n%s",patt,len_p,len_t,testo);
cerca(testo,patt,len_t,len_p);
}
/*ESERCIZIO 29:
A partire dalla matrice A(mxn), del tipo sotto indicato, allocata per righe:
-staticamente,
-dinamicamente
visualizzarne gli elementi per colonne:
11 12 13 14 15 16
21 22 23 24 25 26------>A[4][6]-->[4 righe]x[6 colonne]
31 32 33 34 35 36
41 42 43 44 45 46
Gli elementi a[i][j] della matrice sono tali che le unita' indicano la colonna e
le decine indicano la riga cui l'elemento appartiene*/
#include <stdio.h>
#include <stdlib.h>
#define M 4 /*questo e' il numero di righe*/
#define N 6 /*questo e' il numero di colonne*/
void alloc_static(int a[][]);
void alloc_dinamic(int *pa,int a[][]);
void main()
{
int a[M][N];/*questa variabile mi serve per l'allocazione statica*/
int *pa;/*questa variabile mi serve per l'allocazione dinamica*/
alloc_static(a);
alloc_dinamic(pa,a);
}
}
- 34 -
}
printf("\n");
}
}
Screenshot esercizio 29
- 35 -
/*ESERCIZIO 30
Scrivere una function C che restituisca la matrice C prodotto righexcolonne [vedi pdf delle dispense]
di due matrici rettangolari A e B le cui dimensioni sono stabilite in input (usare per tutte le matrici
l'allocazione dinamica e generarle come numeri reali random). C'e' qualche preferenza nell'usare
malloc() o calloc() rispettivamente per A, B o C? Verificare se i tempi di esecuzione, per la sola
allocazione e totali, sono gli stessi*/
/*Ricordiamoci che per fare il prodotto righexcolonne di due matrici rettangolari bisogna seguire
questa formula: C[M][N]=A[M][P]xB[P][N]
Da questa formula vediamo che:
M e' il numero di righe della matrice finale, ma anche il numero di righe di A
P e' il numero di colonne di A ma anche il numero di righe di B
N e' il numero di colonne della matrice finale, ma anche il numero di colonne di B*/
#include<stdio.h>
#include<stdlib.h>/*stdlib ci serve per le funzioni di allocazione malloc e calloc
ma anche per generare numeri casuali con rand() e srand()*/
#include<time.h>/*ci serve per calcolare i tempi di esecuzione con malloc e calloc attraverso time()*/
#include<windows.h>/*questa libreria mi serve per usare la funzione per il tempo
QueryPeformanceCounter,che e' solo per Windows ed e' accurata ai nanosecondi*/
void matrice_A(int *a, int m, int p, int r, int f);
void matrice_B(int *b, int p, int n, int r, int f);
void matrice_C(int *a, int *b, int *c, int m, int n,int p);
void main()
{
int *a, *b, *c;
int f=1,r=10;/*questi sono gli estremi dell'intervallo dei valori che voglio generare random*/
int m,p,n;
matrice_A(a,m,p,r,f);
matrice_B(b,p,n,r,f);
matrice_C(a,b,c,m,n,p);
printf("\n\nL'allocazione con calloc richiede %g secondi\n",elapsed_Time1);
printf("L'allocazione con malloc richiede %g secondi\n",elapsed_Time2);
if(elapsed_Time1==elapsed_Time2)/*se i due tempi coincidono...*/
{
printf("\nI tempi di esecuzione tra malloc e calloc non cambiano");
}
else
{
printf("\nI tempi di esecuzione tra malloc e calloc cambiano");
}
}
/*stampiamo la matrice A con i due cicli for annidati*/
printf("\n\n***MATRICE A[M][P] generata %d-righe e %d-colonne***\n\n",m,p);
printf("\t\t");
for(i=0;i<m;i++)
{
for(j=0;j<p;j++)
{
printf("%d ",*(a+i*p+j));
}
printf("\n\t\t");
}
}
- 37 -
}
/*stampiamo la matrice B con i due cicli for nidificati*/
printf("\n***MATRICE B[P][N] generata %d-righe e %d-colonne***\n\n",p,n);
printf("\t\t");
for(i=0;i<p;i++)
{
for(j=0;j<n;j++)
{
printf("%d ",*(b+i*n+j));
}
printf("\n\t\t");
}
}
void matrice_C(int *a, int *b, int *c, int m, int n, int p)
{
int i,j,l;/*contatori*/
/*inizializziamo a 0 le componenti della matrice finale C per evitare la presenza di numeri
sporchi*/
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
{
*(c+i*n+j)=0;
}
}
printf("\n**************************************************");
/*Adesso facciamo il prodotto righexcolonne delle due matrici A e B,
cosi' da ottenere la matrice finale C*/
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
{
for(l=0;l<p;l++)
{
*(c+i*n+j)=*(c+i*n+j)+*(a+i*p+l)*(*(b+l*n+j));
}
}
}
}
- 38 -
Screenshot esercizio 30
/*ESERCIZIO 33:
Scrivere una function C che legga, mediante una variabile “buffer” di 200char,
un file testo e lo visualizzi sullo schermo 40 char per riga e 25 righe per ogni schermata,
fermandosi finché non viene premuto un tasto per continuare.*/
/*Ricordiamo che per lettura bufferizzata si intende che i caratteri contenuti
nel file di testo sono letti, in questo caso viene letto un carattere per volta.
Mettiamo a fuoco queste cose. Dobbiamo avere:
1)40 caratteri per riga
2)1 schermata corrisponde a 25 righe */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define BUFSIZE 200/*Size buffer dichiarato come costante globale per la lettura dei caratteri*/
void main()
{
int i_buf; /*indice del buffer*/
int c_riga=0; /*contatore di caratteri per riga, inizializzato a zero*/
int c_schermata=0; /*contatore di righe per schermata, inizializzato a zero*/
int i,j;/*il contatore i lo uso come indice del buffer nella stampa,
il contatore j lo uso per visualizzare l'indice delle righe su schermo*/
int r=1,s=1;/*r mi indica la riga, s la schermata*/
char ch;/*mi serve per leggere i singoli caratteri del testo*/
char buffer[BUFSIZE];/*questo e' l'array che mi contiene i caratteri del testo nel file*/
char nomefile[20]; /*nomefile e' l'array che conterra' il nome del file*/
FILE *fp; /*fp è un puntatore ad una struttura contenente informazioni sul file.
Serve per indirizzare tutte le operazioni sul file*/
printf("\nRicorda che il file da leggere si chiama 'Dante'(il file e' di tipo txt)\n");
printf("Inserire nome del file da leggere: ");
scanf("%s",&nomefile);
strcat(nomefile,".txt");/*concatena al nome del file immesso l'estensione txt blocco note
in modo tale che il file possa essere letto con un editor di testo
riconosciuto in automatico dal sistema operativo*/
printf("nomefile: %s\n", nomefile);
/*se il file non e' accessibile, il programma si arresta*/
if ((fp=fopen(nomefile,"r")) == NULL)
{
puts("Errore apertura file");
exit(1);
}
- 39 -
printf("\n\nSchermata %d\n",s);
/*Il ciclo while viene ripetuto finche' non si giunge alla fine del file*/
while (!feof(fp))
{
i_buf=0;/*azzeriamo inizialmente l'indice del buffer*/
/*finche' l'indice buffer e' minore del massimo size(cioe' finche' il buffer non e' pieno)
e finche' non siamo arrivati alla fine del file...*/
while(i_buf<BUFSIZE && !feof(fp))
{
ch=(char)getc(fp);/*Assegna a ch il carattere del file testo puntato da fp*/
buffer[i_buf] = ch;/*memorizziamo nel buffer il carattere letto*/
i_buf++;/*incrementiamo l'indice del buffer*/
}
/*Incrementa la variabile che tiene conto dei caratteri stampati per schermata*/
c_schermata = c_schermata + i_buf;
i=0;
/*ESERCIZIO 36:
Simulare in C la gestione di una pila (stack) tramite array statico (può essere anche un array di
struct) creando le funzioni di manipolazione push() [inserimento] e pop() [eliminazione].
Il programma deve prevedere un menù che consenta di scegliere l’operazione da eseguire.
__
a | |__| n -->TESTA DELLO STACK: contiene l'indice nell'array dell'ultima
r | |__| informazione inserita nello stack: e' qui che avvengono le
r | |__| fasi di push e pop
a | |__|
y | |__| 1 */
/*Annotazioni essenziali sulla struttura della pila:
La PILA detta anche stack, è una struttura lineare aperta in cui l'accesso alle componenti
per l'inserimento e l'eliminazione avvengono solo ad un estremo della struttura(detta testa della
pila).
La pila è una struttura LIFO(LAST INPUT FIRST OUTPUT), perché l'ultimo elemento inserito è
il primo ad essere eliminato.Simuliamo lo stack con un array statico, in quanto esso puo
essere immaginato appunto come un array rovesciato: l'inserimento e l'eliminazione che avvengono
alla testa dello stack si verificano sull'ultima componente usata dall'array.
*/
#include<stdio.h>
#define MAX_STACK_SIZE 100/*il nostro stack puo' contenere massimo 100 elementi*/
void push_s(char elem,char p_stack[], short *head );
void pop_s(char *elem, char p_stack[], short *head);
void main()
{
int scelta;/*questa variabile ci consente di fare la scelta nel menu'*/
int i;/*indice*/
char elem;/*l'elemento di tipo char che vogliamo inserire/prelevare nello stack*/
char stack[MAX_STACK_SIZE];/*l'array che mi contiene gli elementi della pila*/
short head=-1;/*la testa dello stack inizializzata a -1 mi indica stack vuoto*/
elem=(char)(65);/*il primo elemento da inserire e' la lettera A maiuscolo
che nel codice ASCII vale 65*/
printf("Che cosa vuoi fare?\nMetti sotto alle possibili scelte la tua scelta: \n");
do{
printf("\n[1]Inserisci un elemento con push");
printf("\n[2]Elimina un elemento con pop");
printf("\n[3]Esci dal programma");
printf("\nScelta: ");
scanf("%d",&scelta);
system("cls");/*elimina l'inutile dallo schermo generato dal continuo ciclo del while*/
switch(scelta)
{
case 1: /*Se la pila si riempie del tutto*/
if(head==MAX_STACK_SIZE-1)
{
printf("\nPila piena, impossibile inserire\n");
}
else
{
push_s(elem,stack,&head);
/*la testa aggiunge un elemento allo stack:
questo elemento viene inserito nella prima componente del
nostro vettore e, ad ulteriori inserimenti, l'elemento
viene inserito nelle successive componenti del vettore*/
for(i=head;i>=0;i--)
{
printf("a[%d]=%c\n",i,stack[i]);
}
elem++;/*incremento il valore di elem cosi' da avere 66,67,68, ecc
che nel codice ASCII valgono rispettivamente B,C,D, ecc*/
}
break;
- 41 -
case 2:
pop_s(&elem,stack,&head);
/*Se la pila e' vuota non e' possibile eliminare*/
if(head==-1)
{
printf("\nPila vuota, impossibile eliminare\n");
}
else
{
/*stampami gli elementi che rimangono e che
non sono stati ancora eliminati*/
for(i=head;i>=0;i--)
printf("a[%hd]=%c\n",i,stack[i]);
}
break;
case 3:
printf("\nArrivederci");
break;
}
}while(scelta!=3);/*finche' non premo 3 non esco dal programma*/
void push_s(char elem, char p_stack[], short *head)/* *head e' il puntatore alla testa della pila,
cioe' all'estremo dove vegono effettuate le operazioni di push e pop*/
{
if(*head<MAX_STACK_SIZE-1)/*perche' ricordiamo che partiamo da 0
per arrivare a 99, ottenendo il max_size=100*/
*(p_stack+ ++*head)=elem;/*il puntatore alla testa viene prima incrementato per poi essere
utilizzato per stabilire la posizione della componente in cui andare ad inserire il valore
dell'elemento elem*/
}
void pop_s(char *elem, char p_stack[], short *head)
{
if(*head!=-1)/*si entra nell'if solo se lo stack non e' vuoto*/
*elem=*(p_stack+(*head)--);/*il valore di head viene prima utilizzato per prelevare
l'informazione e restituirla nella variabile puntata da elem, quindi ritornarla come parametro di
uscita della function e successivamente viene decrementato.Quest' ordine temporale delle operazioni e'
realizzato mediante gli operatori di pre-incremento e post-decremento*/
}
Screenshot esercizio 36
Inserimento Eliminazione
- 42 -
/*ESERCIZIO 37:
Simulare in C la gestione di una coda (queue) tramite array statico (può essere anche un array di
struct) creando le funzioni di manipolazione enqueue() [inserimento] e dequeue() [eliminazione].
Il programma deve prevedere un menù che consenta di scegliere l’operazione da eseguire.
Le informazioni NON vanno spostate!
a | |___| n
r | |___| -->FONDO: contiene l'indice nell'array della
r | |_C_| prima componente libera(dove inserire la prossima
a | |_B_| informazione
y | |_A_| 1--->TESTA: contiene l'indice nell'array dell'informazione
di testa della coda(la prossima da eliminare)*/
/*Annotazioni utili sulla coda:
La coda è una struttura lineare aperta in cui l'accesso alle componenti avviene solo ai due estremi:
-l'eliminazione avviene solo all'inizio della struttura(testa).
-l'inserimento avviene solo alla fine(fondo).
La coda si presenta come una struttura FIFO( FIRST INPUT FIRST OUTPUT) perché il primo elemento
inserito è il primo ad essere eliminato.
La fase di eliminazione si chiama dequeue e la fase di inserimento enqueue.
A differenza dello stack, la coda tende a slittare verso la fine dell'array in quanto
l'inserimento e l'eliminazione avvengono in due estremi opposti: quindi arriveremo prima
o poi all'overflow di array.
Nel caso della coda, tutte le componenti dell'array che si liberano in seguito ad eliminazione di
informazioni, diventano spazio inutilizzabile e quindi la coda più facilmente della pila tende a
raggiungere il massimo riempimento. */
#include <stdio.h>
#define MAX 10/*la coda puo' contenere massimo 10 elementi*/
void enqueue(char *elem,char p_coda[],int *bottom);
void dequeue(int *head);
void main()
{
char coda[MAX]; //stack di MAX 10 elementi
int scelta;
int head=0,bottom=0;//head mi rappresenta la testa della coda, bottom invece e' il fondo
//head e fondo sono inizializzate a 0 perche’ sono vuote
char elem=(char)65;/*l'elemento di tipo char che vogliamo inserire/prelevare nello stack:
il primo elemento da inserire e' la lettera A maiuscoloche nel codice ASCII vale 65*/
int i;
do
{
printf("\nMENU' CODA:\n");
printf("[1]Inserisci un elemento nel FONDO con enqueue\n");
printf("[2]Elimina un elemento nella TESTA con dequeue\n");
printf("[3]Esci dal programma");
printf("\nScelta: ");
scanf("%d",&scelta);
system("cls");
switch(scelta)
{
case 1:
if(bottom<MAX)//Se non andiamo oltre il massimo size della coda
{
enqueue(&elem,coda,&bottom);
/*la visualizzazione della coda parte dal fondo dove inserisco
fino ad arrivare alla testa dove prelevo*/
for(i=bottom;i>=head;i--)
{
printf("a[%d]=%c\n",i,coda[i]);
}
elem++;/*incremento il valore di elem cosi' da avere 66,67,68, ecc
che nel codice ASCII valgono rispettivamente B,C,D, ecc*/
bottom++;//incremento il fondo ogni volta che inserisco un elemento
break;
}
else
{
printf("\nCoda piena, impossibile inserire\n");
break;
}
- 43 -
case 2:
if(bottom!=0)//se il fondo non e' vuoto
{
dequeue(&head);
/*la visualizzazione della coda parte dal fondo dove inserisco
fino ad arrivare alla testa dove prelevo*/
for(i=bottom-1;i>=head;i--)
{
printf("a[%d]=%c\n",i,coda[i]);
}
break;
}
else
{
printf("\nNon c'e' nessun elemento nel fondo. Impossibile eliminare");
break;
}
case 3:printf("\nArrivederci");
break;
}
}while(scelta!=3);
}
Screenshot esercizio 37
Inserimento Eliminazione
- 44 -
/*ESERCIZIO 39
Simulare in C l’algoritmo di visita di una lista lineare già memorizzata mediante un array
statico di struct (come nella tabella in basso) in cui il primo campo contiene l’informazione
ed il secondo contiene il link al nodo successivo (in questo caso il link è l’indice di una
componente dell’array). Memorizzando nell’array i dati come mostrato nella figura che segue,
l’output del programma consiste nell’elenco di nomi ordinato alfabeticamente.
#include<stdio.h>
#include<string.h>
/*creazione della struct globale, da poter utilizzare all'interno sia del main che della function,
che rappresenta un nodo della lista che conterra' i nostri 11 elementi*/
struct elenco
{
char nome[11];/*questo campo contiene l'informazione cioe' il nome della persona*/
short pnext;/*questo campo contiene i link al prossimo nodo cioe' all'informazione successiva*/
};
typedef struct elenco ELENCO;/*definiamo piu' chiaramente la struttura elenco con typedef*/
/*definiamo con un array statico di struct l'elenco non ordinato con campo nome e pnext,
dove pnext contiene il puntatore all'elemento successivo in ordine alfabetico*/
ELENCO persone[11] = { {"Anna",5},
{"Mario",8},
{"Giuseppe", 6},
{"Angela",0},
{"Valeria",-1},
{"Fabrizio",7},
{"Marianna",1},
{"Giovanni",2},
{"Patrizia",10},
{"Valentina",4},
{"Sara",9}};
/*ESERCIZIO 41
Realizzare la gestione di una lista lineare mediante menù (visualizzazione mediante visita,
inserimento in testa, inserimento in mezzo, eliminazione in testa, eliminazione in mezzo)
implementando la lista lineare con una struttura autoriferente dinamica.*/
/*Cose fondamentali da ricordare:
-INSERIMENTO IN MEZZO
Per inserire un elemento dopo quello corrente, bisogna:
1)inserire il link che va dall'elemento nuovo al successore
2)modificare il link che va dall'elemento nuovo al successore
-INSERIMENTO IN TESTA
Ricordiamo che per inserire in testa bisogna:
1)inserire il link che va dall'elemento nuovo alla testa della struttura
2)modificare il link alla testa affinche' punti al nuovo elemento
-ELIMINAZIONE IN MEZZO
Per eliminare in mezzo bisogna modificare il link del predecessore affinche' punti al successore
-ELIMINAZIONE IN TESTA
Ricordiamo che per eliminare in testa bisogna eliminare l'elemento in testa, cioe' bisogna modificare
il link che punta all'inizio della
struttura*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
/*stabiliamo la struttura che mi contiene le informazioni di ogni nodo: queste sono nome ed eta'*/
typedef struct
{
char nome[20];
int eta;
}INFO_FIELD;
printf("\n\nMENU' LISTA\n");
printf("\n[1]Visualizza la lista");
printf("\n[2]Inserisci un elemento in testa");
printf("\n[3]Inserisci un elemento in mezzo(dopo il nodo corrente)");
printf("\n[4]Elimina un elemento in testa");
printf("\n[5]Elimina un elemento in mezzo(dopo il nodo corrente)");
printf("\n[6]Esci dal programma");
printf("\n\nScelta: ");
scanf("%d",&scelta);
system("cls");
switch(scelta)
- 47 -
{
case 1:
punt=head;/*il puntatore punt punta alla testa quindi punta al primo nodo e
parte da qui per visualizzare l'intera lista e per permettere le varie
operazioni*/
if(head==NULL)/*se la testa e' vuota*/
{
printf("\nLa lista e' vuota\n");
break;
}
else
{
case 2:
printf("\nInserisci il nome: ");
scanf("%s",&nuovo_dato.nome);
printf("\nInserisci l'eta': ");
scanf("%d",&nuovo_dato.eta);
inserisci_in_testa(nuovo_dato,&head);
i++;//incrementiamo perche' abbiamo inserito
break;
case 3:
if(i<1)/*se non ho inserito almeno un elemento non posso inserire nel mezzo*/
{
printf("\nNon puoi inserire finche' non immetti almeno un elemento in
testa alla lista\n");
break;
}
else
{
printf("\nInserisci il nome: ");
scanf("%s",&nuovo_dato.nome);
printf("\nInserisci l'eta': ");
scanf("%d",&nuovo_dato.eta);
printf("\nDopo quale nodo corrente vuoi inserire il nuovo
dato?\n(inserisci il nome)Il dato va inserito dopo questo nodo: ");
fflush(stdin);
scanf("%s",testo);
punt = head;//la variabile punt punta alla testa della lista
cerca_nodo(&punt,testo);//chiamata per verificare la presenza/non
presenza del dato che deve essere il nodo corrente
if(!punt)//se non ho trovato l'informazione corrente dopo la quale
immettere il nuovo dato
{
printf("\nIl nodo corrente da cercare non e' stato trovato");
break;
}
inserisci_in_mezzo(nuovo_dato,punt);
printf("\nINSERIMENTO AVVENUTO CON SUCCESSO!");
i++;//incrementiamo perche' abbiamo inserito
break;
}
case 4:
- 48 -
case 6:printf("\nArrivederci");
break;
}
}while(scelta!=6);
}
/*dato rappresenta l'informazione che noi desideriamo copiare nel primo nodo della lista e **p_head
rappresenta il puntatore alla testa della lista. head e' di per se un puntatore ma in questo caso
l'inserimento in testa coinvolge la modifica di questo puntatore e quindi non e' solo un puntatore
ma anche una variabile di uscita perche' Il C prevede che i parametri di uscita di una function debbano
essere passati per riferimento, allora e'necessario dichiarare il parametro formale che sia di uscita e
che sia unpuntatore con il doppio asterisco perche' un asterisco e' perche' e' un parametro di uscita e
l'altro asterisco e' perche' e' un puntatore*/
void inserisci_in_testa(INFO_FIELD dato, struct PERSONA **p_head)
{
struct PERSONA *ptr;/*ptr mi serve a definire la posizione del primo nodo nella lista*/
ptr=calloc(1,sizeof(struct PERSONA));/*alloca un blocco di memoria di ampiezza pari alla
struttura di un nodo*/
ptr->info=dato;/*definisce il campo informazione*/
ptr->p_next=*p_head;/*copia nel campo pnext(quindi nel nodo successivo) l'indirizzo di memoria
di quello che prima era il primo nodo e quindi era puntato dalla variabile p_head*/
*p_head=ptr;/*aggiorna head al nuovo nodo*/
}
{
struct PERSONA *head;
head=NULL;
return head;
}
Screenshot esercizio 41
Screenshot inserimento in testa Screenshot inserimento nel mezzo
/*ESERCIZIO 44
Realizzare in C le funzioni per la gestione, mediante menù, delle strutture dati pila e coda
mediante lista lineare dinamica e generica con [rispettivamente senza] nodo sentinella.
In questo caso FACCIAMO LISTA LINEARE GENERICA SENZA NODO SENTINELLA */
/*Ricordiamo che il nodo sentinella serve per evitare di avere due function per l'inserimento e due
function per l'eliminazione. Infatti è possibile avere un'unica sequenza di operazioni sia per gli
inserimenti/eliminazioni sulla testa che per quelli fatti sul nodo corrente.
Occorre semplicemente aggiugere un nodo fittizio che funga da testa ma che punti alla testa
reale della lista: un nodo sentinella.
Ogni modifica sulla testa comporterà in questo modo alla sola alterazione del campo puntatore del
nodo sentinella*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*stabiliamo la struttura che mi contiene le informazioni: l'informazione e' il campo nome*/
typedef struct
{
char nome[20];
} INFO_FIELD;
head = crea_lista();/*inizializziamo la lista rendendola vuota cioe' la testa della lista non
contiene nessun elemento*/
switch(scelta)
{
case 0:
printf("\nFINE PROGRAMMA!\n");
break;
case 1:
if (head == NULL)//se la testa e' vuota
printf("\nPILA VUOTA\n");
else
{
punt = head; //per la visualizzazione
printf("\nVisita della pila\n\n");
visita((void *)punt);
}
break;
case 2:
printf("\nInserisci il nome: ");
fflush(stdin);
gets(nuovodato.nome);
insl_testa(len_info, &nuovodato, (void **)&head);
printf("Inserimento nella pila effettuato\n");
break;
case 3:
if (head == NULL)//se la testa e' vuota
printf("\nPILA VUOTA - IMPOSSIBILE ELIMINARE ELEMENTI\n");
else
{
printf("Eliminazione in testa...\n");
elim_testa((void **)&head);
}
break;
}
}while (scelta != 0);
break;
}
case 2:
{
system("cls");
do
{
printf("\n\t MENU CODA:\n0 per uscire dal programma \n1 per la visualizzazione della
coda \n2 per l'inserimento nella coda enqueue()""\n3 per l'eliminazione dalla coda
dequeue()\n");
printf("\nscelta: ");
fflush(stdin);
scanf("%d", &scelta);
switch(scelta)
{
case 0:
printf("\nFINE PROGRAMMA!\n");
break;
case 1:
if (head == NULL)//se la testa e' vuota
printf("\nCODA VUOTA\n");
else
{
punt = head; //per la visualizzazione
printf("\nVisita della coda\n\n");
visita((void *)punt);
}
break;
case 2:
if (head == NULL)//se la testa e' vuota
- 52 -
{
printf("Inserisci il nome: ");
fflush(stdin);
gets(nuovodato.nome);
insl_testa(len_info, &nuovodato, (void **)&head);
bottom = head;/*con l'inserimento in testa, il fondo==testa
printf("Inserimento nella coda effettuato\n");
break;
}
else
{
if (bottom != NULL)//se il fondo non e' vuoto
{
printf("Inserisci il nome: ");
fflush(stdin);
gets(nuovodato.nome);
insl_nodo(len_info, &nuovodato, (void **)&bottom);
printf("Dato inserito in coda!\n");
break;
}
}
case 3:
if (head == NULL)//se la testa e' vuota
printf("\nCODA VUOTA - IMPOSSIBILE ELIMINARE ELEMENTI\n");
else
{
printf("Eliminazione in testa...\n");
elim_testa((void **)&head);
}
break;
}
}while (scelta != 0);
break;
}
case 0:
printf("\nFINE PROGRAMMA\n");
break;
}
} while (scelta != 0);
}
/*inizializzo la lista svuotandola*/
void *crea_lista()
{
char *testa;
testa = NULL;
return testa;
};
- 53 -
((struct lista *)*p_punt)->p_next = ptr; //il nodo p_punt e' agganciato a ptr
/*questa funzione ci consente di visualizzare gli elementi contenuti sia nella coda che nella pila,
attraverso il puntatore punt*/
void visita(void *punt)
{
/*si definisce una struttura lista generica che condivide con il main solo il tipo
INFO_FIELD(globale)*/
Struct lista
{
INFO_FIELD info;
struct lista *p_next;
} *ptr;
ptr = punt;
while (ptr->p_next != NULL)/*finche' non siamo arrivati alla fine della lista*/
{
printf("NOME = %s p_next= %d \n", (*ptr).info.nome, (*ptr).p_next);
ptr = ptr->p_next;/*assegna a punt il link (indice) del nodo successivo contenuto nel
campo pnext del nodo corrente*/
}
/*usciti dal while, ci troviamo sull'ultimo nodo e allora l'ultima printf visualizza i campi
dell'ultimo nodo*/
printf("NOME = %s p_next= %d \n", (*ptr).info.nome, (*ptr).p_next);
}
/*lo scopo di questa funzione e' l'eliminazione del nodo puntato da ptr e che vogliamo considerare*/
void elim_testa(void **p_head)
{
/*si definisce una struttura lista generica che condivide con il main solo il tipo
INFO_FIELD(globale)*/
struct lista
{
INFO_FIELD info;
struct lista *p_next;
} *ptr;
ptr = ((struct lista *)*p_head)->p_next;//facciamo puntare head al nodo successivo per tagliare
la vecchia testa*/
PILA CODA
- 55 -
/*In questo caso GESTIAMO LA PILA CON LA LISTA DINAMICA(CON NODO SENTINELLA)*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*stabiliamo la struttura che mi contiene le informazioni di ogni nodo: abbiamo l'unico campo nome*/
typedef struct{
char nome[20];
} INFO_FIELD;
INFO_FIELD nuovodato;//campo informazioni che verrà inserito nel nuovo nodo da creare
len_info=sizeof(INFO_FIELD);
printf("\n0 per l'uscita dal programma\n1 per la visualizzazione della pila \n2 per
l'inserimento nella pila push()" "\n3 per l'eliminazione dalla pila pop()");
printf("\nScelta: ");
scanf("%d",&scelta);
system("cls");
switch(scelta)
{
case 0:
printf("\nFINE PROGRAMMA!\n");
break;
case 1:
/*Stampa se dopo il nodo sentinella c'è qualcosa.
Inizia a stampare dal nodo successivo sentinella*/
if(head->p_next)
{
visita((void *)(head)->p_next); //stampa la lista dopo ogni operazione
break;
}
else
{
printf("\nLa pila e' vuota!");
break;
}
- 56 -
case 2:
// Inserisce dopo nodo sentinella, senza utilizzare "insl_testa"
printf("\nInserisci il nome ");
fflush(stdin);
gets(nuovodato.nome);
ins_nodo(len_info, &nuovodato, (void *)head);
printf("\nInserimento nella pila effettuato\n");
break;
case 3:
//Elimina il nodo dopo il nodo sentinella, senza utilizzare "elim_testa"
if (head->p_next!= NULL)// Se dopo il nodo sentinella c'è qualcosa
{
printf("Eliminazione in testa...\n");
elim_nodo((void *)head);
}
else
{
printf("\nPILA VUOTA - IMPOSSIBILE ELIMINARE ELEMENTI\n");
}
break;
}
}while(scelta!=0);
}
- 57 -
/*questa funzione ci consente di visualizzare gli elementi contenuti sia nella lista che nella pila,
attraverso il puntatore punt*/
void visita(void *p_punt)
{
struct lista
{
INFO_FIELD info;
struct lista *p_next;
} *ptr;
ptr = ((struct lista*)p_punt);
while (ptr!= NULL)/*finche' non siamo arrivati alla fine della lista*/
{
printf("NOME = %s p_next= %d \n", (*ptr).info.nome,(*ptr).p_next);
ptr = ptr->p_next;/*assegna a punt il link (indice) del nodo successivo contenuto nel campo
pnext del nodo corrente*/
}
Screenshot esercizio 44
/*In questo caso GESTIAMO LA CODA CON LA LISTA DINAMICA(CON NODO SENTINELLA)*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*stabiliamo la struttura che mi contiene le informazioni di ogni nodo: abbiamo l'unico campo nome*/
typedef struct{
char nome[20];
} INFO_FIELD;
INFO_FIELD nuovodato;//campo informazioni che verrà inserito nel nuovo nodo da creare
len_info=sizeof(INFO_FIELD);
printf("\n0 per l'uscita dal programma\n1 per la visualizzazione della coda \n2 per
l'inserimento nella coda enqueue()""\n3 per l'eliminazione dalla coda dequeue()");
printf("\nScelta: ");
scanf("%d",&scelta);
system("cls");
switch(scelta)
{
case 0:
printf("\nFINE PROGRAMMA!\n");
break;
case 1:
/*Stampa se dopo il nodo sentinella c'è qualcosa.
Inizia a stampare dal nodo successivo sentinella*/
if(head->p_next)
{
visita((void *)(head)->p_next); //stampa la lista dopo ogni operazione
break;
}
else
{
printf("\nLa coda e' vuota!\n");
break;
}
- 59 -
case 2:
//La CODA inserisce in fondo, ma estrae dalla testa
// Inserisce dopo nodo sentinella, inserendo il nodo dal fondo
printf("\nInserisci il nome ");
fflush(stdin);
gets(nuovodato.nome);
/* Fondo punta all'ultimo valore inserito.
Quando inserisco un elemento dopo fondo, fondo si aggiorna all'indirizzo
del nuovo nodo: quindi quando riinserirò a partire da quell'indirizzo
che indica il nodo alla fine, inserirà alla fine e fondo si riaggiornerà */
ins_nodo(len_info, &nuovodato, (void **)&fondo);
printf("Inserimento nella coda effettuato\n");
break;
case 3:
//Elimina il nodo dalla testa della coda
if (head->p_next!= NULL)// Se dopo il nodo sentinella c'è qualcosa
{
/* Head punta a sentinella, quindi elimina elemento dopo sentinella.
Head punterà sempre al nodo sentinella, quindi non necesito di modificarlo */
printf("Eliminazione in testa...\n");
elim_nodo((void *)head);
}
else
{
printf("\nPILA VUOTA - IMPOSSIBILE ELIMINARE ELEMENTI\n");
}
break;
}
}while(scelta!=0);
}
/*questa funzione ci consente di visualizzare gli elementi contenuti sia nella lista che nella pila,
attraverso il puntatore punt*/
void visita(void *p_punt)
{
struct lista
{
INFO_FIELD info;
struct lista *p_next;
} *ptr;
ptr = ((struct lista*)p_punt);
while (ptr!= NULL)/*finche' non siamo arrivati alla fine della lista*/
{
printf("NOME = %s p_next= %d \n", (*ptr).info.nome,(*ptr).p_next);
ptr = ptr->p_next;/*assegna a punt il link (indice) del nodo successivo contenuto nel campo
pnext del nodo corrente*/
}
}
Screenshot esercizio 44
/*ESERCIZIO 49
Scrivere le function C per la visita (preorder, inorder e postorder)
di un albero binario rappresentato mediante array*/
/*-VISITA PREORDER(ordine anticipato) che visita in ordine :
1)la radice
2)il sottoalbero sinistro
3)il sottoalbero destro
-VISITA INORDER(ordine simmetrico) che visita in ordine:
1)il sottoalbero sinistro
2)la radice
3)il sottoalbero destro
-VISITA POSTORDER(ordine differito) che visita in ordine:
1)il sottoalbero sinistro
2)il sottoalbero destro
3)la radice */
#include<stdio.h>
void visita_preorder(int array[],int i);
void visita_postorder(int array[],int i);
void visita_inorder(int array[],int i);
void main()
{
int i, j=0, num_nodi=7;
int array[7];
for(i=1;i<=7;i++)
{
array[i]=j;
j++;
}
printf(" ALBERO BINARIO\n\n");
printf(" (%d)\n",array[1]);
puts(" / \\");
puts(" / \\");
puts(" / \\");
printf(" (%d) (%d)\n",array[2],array[3]);
puts(" / \\ / \\ ");
printf(" (%d) (%d) (%d) (%d) \n",array[4],array[5],array[6],array[7]);
i=1;
j=0;
printf("\nVISITA PREORDER\n");
visita_preorder(array,i);
printf("\n\nVISITA POSTORDER\n");
visita_postorder(array,i);
printf("\n\nVISITA INORDER\n");
visita_inorder(array,i);
}
/*L'algoritmo di visita di un albero binario in generale deve prevedere una struttura dati di
appoggio, uno "STACK", il quale conserva l'informazione sull'ordine dei nodi di cui non e'
stata ancora completata la visita. Esso deve essere gestito direttamente dal programmatore
nell'algoritmo iterativo, mentre viene gestito dal linguaggio di programmazione quando
l'algoritmo e' descritto in maniera ricorsiva. Noi adottiamo la maniera ricorsiva*/
/*function ricorsiva relativa alla visita del sottoalbero sinistro finche' non e' stato visitato
fino alle foglie*/
visita_preorder(array, 2*i);
/*function ricorsiva relativa alla visita del sottoalbero destro finche' non e' stato visitato fino
alle foglie*/
visita_preorder(array, 2*i+1);
}
- 62 -
/*ESERCIZIO 55
Scrivere function C per la costruzione di un grafo non orientato mediante matrice di adiacenze:
in input per ogni nodo sono specificati quelli adiacenti.
Scegliendo in input un nodo, scrivere una function C che restituisca il suo grado.*/
/*Ricordiamo alcune cose fondamentali sul grafo non orientato:
Il grafo non orientato e' un grafo in cui non e' stato assegnato un verso di percorrenza,
quindi la percorrenza da un nodo a un altro e' a doppio senso. La matrice adiacente,
che e' la rappresentazione matematica del grafo e' una matrice simmetrica: cio' significa che se
noi potessimo tagliarla sulla diagonale principale, il triangolo superiore è simmetrico rispetto al
triangolo inferiore */
#include<stdio.h>
/*dichiarazione della struttura globale del singolo nodo del grafo con il campo informazione che e' int
( ho preferito i numeri) e il campo grado di tipo intero*/
struct nodo
{
int info;
int grado;
};
typedef struct nodo NODO;/*definiamo il tipo NODO della struttura struct nodo*/
int num_nodi=0;/*variabile globale che mi indica il numero di nodi totali presenti nel grafo*/
}while(num_nodi<2);
NODO nodi[num_nodi];/*variabile struttura di tipo array che ci serve per effettuare le varie
operazioni sulla matrice*/
int i,j;
int adiacenza;/*questa variabile serve per definire le adiacenze scelte dall'utente*/
int k=1;/*k mostra all'utente le adiacenze(nodi rispettivi adiacenti)*/
char car=(char)65;/*questa variabile mi serve per capire meglio i passaggi definendo, per ogni
informazione di un singolo nodo, il relativo carattere. Inizializzo la variabile al primo
carattere dell'alfabeto la A maiuscola*/
/*Se il valore inserito è più alto del numero massimo o minimo dei nodi
oppure è uguale al nodo corrente, allora viene mostrato un ERRORE*/
if(adiacenza>=num_nodi || adiacenza<-1 || adiacenza==nodi[i].info)
{
printf("ERRORE: Valore non valido\n");
}
else if(adiacenza!=-1) /*se diverso dal valore di termine analisi del nodo*/
{
/*Controlla che la cella in cui stiamo inserendo 1, per indicare
l'adiacenza, sia vuota cioe' sia uguale a 0, altrimenti è già
esistente*/
if((matrice_adiacenze[i][adiacenza])==0)
{
(matrice_adiacenze[i][adiacenza])=1;/*inseriamo 1 nella cella che
ha per coordinate di righe il nodo corrente e per coordiante di
colonne il valore stesso dell'adiacenza*/
nodi[i].grado++;/*incrementiamo il valore del grado relativo al
nodo che stiamo considerando*/
k++;/*passiamo alla prossima adiacenza*/
}
- 65 -
else
{
puts("ATTENZIONE: Adiacenza gia' esistente. Reinserire!");
}
}
}while(adiacenza!=-1 && k<num_nodi);
k=1;/*inizializza il numero delle adiacenze nel momento in cui passiamo al prossimo
nodo*/
}
/*ESERCIZIO 56
Scrivere function C per la costruzione di un grafo orientato mediante matrice di adiacenze:
in input per ogni nodo sono specificati quelli raggiungibili. Scegliendo in input un nodo,
scrivere una function C che restituisca il numero degli archi uscenti e quello degli archi entranti*/
/*Ricordiamo alcune cose fondamentali sul grafo orientato:
Il grafo orientato e' un grafo in cui e' stato assegnato un verso di percorrenza,
quindi la percorrenza da un nodo a un altro e' a senso unico. La matrice adiacente,
che e' la rappresentazione matematica del grafo e' una matrice non simmetrica: cio' significa che se
noi potessimo tagliarla sulla diagonale principale, il triangolo superiore non è simmetrico rispetto al
triangolo inferiore*/
#include<stdio.h>
/*dichiarazione della struttura globale del singolo nodo del grafo con il campo informazione che e' int
( ho preferito i numeri)
e il campo entrante e uscente di tipo intero relativi agli archi, che costituiscono le connessioni tra
due nodi adiacenti(dette anche lati)*/
struct nodo
{
int info;
int entrante;
int uscente;
};
typedef struct nodo NODO;/*definiamo il tipo NODO della struttura struct nodo*/
int num_nodi=0;/*variabile globale che mi indica il numero di nodi totali presenti nel grafo*/
puts("GRAFO ORIENTATO\n");
}while(num_nodi<2);
NODO nodi[num_nodi];/*variabile struttura di tipo array che ci serve per effettuare le varie
operazioni sulla matrice*/
int i,j;
int raggiungibile;/*questa variabile serve per definire il nodo raggiungibile scelto dall'utente*/
int k=1;/*k mostra all'utente il numero in successione di nodi raggiungibili*/
char car=(char)65;/*questa variabile mi serve per capire meglio i passaggi definendo, per ogni
informazione di un singolo nodo, il relativo carattere. Inizializzo la variabile al primo carattere
dell'alfabeto la A maiuscola*/
/*Se il valore inserito è più alto del numero massimo o minimo dei nodi
oppure è uguale al nodo corrente, allora viene mostrato un ERRORE*/
if(raggiungibile>=num_nodi || raggiungibile<-1 || raggiungibile==nodi[i].info)
{
printf("ERRORE: Valore non valido\n");
}
else if(raggiungibile!=-1) /*se invece è diverso dal valore di termine analisi del
nodo*/
{
/*Controlla che la cella in cui stiamo inserendo 1, per indicare il nodo
raggiungibile, sia vuota cioe' sia uguale a 0, altrimenti è già esistente*/
if((matrice_adiacenze[i][raggiungibile])==0)
{
(matrice_adiacenze[i][raggiungibile])=1;/*inseriamo 1 nella cella che ha
per coordinate di righe il nodo corrente e per coordiante di colonne il
numero del nodo raggiungibile*/
nodi[i].uscente++;/*incrementa il contatore dell'arco uscente del nodo
corrente*/
nodi[raggiungibile].entrante++;/*incrementa il contatore degli archi
entranti del nodo raggiunto*/
k++;/*passiamo al prossimo nodo raggiungibile*/
}
- 68 -
else
{
puts("ATTENZIONE: Nodo raggiungibile gia' esistente. Reinserire!");
}
}
}while(raggiungibile!=-1 && k<num_nodi);
k=1;/*inizializza il numero dei nodi raggiungibili nel momento in cui passiamo al prossimo
nodo*/
}
}while(scelta!=-1);
}
Screenshot esercizio 56
- 69 -
/*ESERCIZIO 61
Scrivere delle function C (rispettivamente iterativa e ricorsiva) per calcolare
(con ricorsione sia lineare sia binaria) la somma delle componenti di un array.*/
#include<stdio.h>
int ricorsione_binaria(int array[],int primo, int ultimo);
int ricorsione_lineare(int array[],int n);
int somma_iterativa(int array[],int n);
void main()
{
int array[10]={0,1,2,3,4,5,6,7,8,9};
int n=10;/*n e' il size dell'array*/
int i;//contatore
int primo=0;//indice della prima componente del vettore
int ultimo=n-1;//indice della seconda componente del vettore
int somma_ite=0,somma_ric=0,somma_bin=0;/*variabili che,inizializzate tutte a 0, mi conterranno
il valore delle somme relative rispettivamente alla somma iterative, alla somma ricorsiva
lineare e alla somma ricorsiva binaria */
printf("Ecco gli elementi presenti nell'array\n");
for(i=0;i<10;i++)
{
printf("%d ",array[i],i);
}
somma_ite=somma_iterativa(array,n);
printf("\n\nSOMMA ITERATIVA: %d",somma_ite);
somma_ric=ricorsione_lineare(array,n);
printf("\n\nSOMMA RICORSIVA LINEARE: %d\n",somma_ric);
somma_bin=ricorsione_binaria(array,primo,ultimo);
printf("\nSOMMA RICORSIVA BINARIA: %d",somma_bin);
}
/*La ricorsione lineare prevede che nel corpo della funzione ci sia una sola chiamata ricorsiva*/
int ricorsione_lineare(int array[],int n)
{
if(n==1)/*caso banale*/
{
return array[0];/*il primo elemento dell'array e’ il risultato della somma*/
}
else
{
return array[n-1]+ricorsione_lineare(array,n-1);/*andiamo a sommare ricorsivamente
l'elemento contenuto nell'ultima componente dell'array a[9] con il penultimo e
con quelli precedenti, arriveremo alla prima componente a[0],dove risiede il risultato
della somma. Ad ogni chiamata ricorsiva si decrement il valore di n, il size dell'array
per passare agli elementi di indice minore*/
}
}
/*La function ricorsione binaria con l'approccio Divide et Impera che scinde l'istanza
principale in istanze piu' semplici per la risoluzione del problema.
Prevede 2 chiamate ricorsive all'interno del proprio corpo, il problema viene decomposto in
2 sottoproblemi, e poi la chiamata ricorsiva si applica ad entrambi i sottoproblemi le cui
soluzioni vanno sommate per ottenere il risultato finale.
La somma degli elementi di un vettore di n-elementi e' pari alla somma delle somme di due
vettori composti il primo dai primi n/2 elementi ed il secondo dagli altri n/2 elementi.*/
int ricorsione_binaria(int array[],int primo, int ultimo)
{
int mediano;//questo e' l'elemento di mezzo
if(primo==ultimo)//caso banale
{
return array[primo];
}
else
{
/* autoattivazioni */
mediano = (primo+ultimo)/2; /*calcola l'indice mediano per dividere l'array*/
/*sommiamo ricorsivamente le sottoporzioni venitesi a formare per ottenere la soluzione finale*/
return ricorsione_binaria(array,primo,mediano) + ricorsione_binaria(array,mediano+1, ultimo);
}
}
- 70 -
Screenshot esercizio 61
/*ESERCIZIO 62
Scrivere delle function C (rispettivamente iterativa e ricorsiva) per calcolare
(con ricorsione sia lineare sia binaria) la potenza intera x^n di un numero reale.*/
#include<stdio.h>
#include<math.h>
int ricorsione_lineare(int x,int n);
int ricorsione_binaria(int x,int n);
int pot_iterativa(int x, int n);
void main()
{
int x=2;//questo e' il valore
int n=3;//questa e' la potenza alla quale viene elevato il valore
int i;//contatore
int numero=pow(2,3);
Screenshot esercizio 62
- 72 -
/*ESERCIZIO 67
Scrivere due function C (rispettivamente iterativa e ricorsiva) per implementare l’algoritmo Selection
Sort
su un array di struttura, sia mediante scambi reali sia mediante scambi virtuali*/
/*Definiamo il concetto di...
-scambi reali: si hanno quando i dati sono fisicamente scambiati di posto. Per scambiare l'informazione
vera e propria si richiede di scambiare non solo la chiave ma tutte le informazioni collegate a quella
chiave.
-scambi virtuali: si hanno quando i dati non sono fisicamente scambiati perché si opera tramite
puntatori: questo si fa per evitare di perdere tempo se abbiamo dati cospicui. Noi abbiamo le
informazioni ma per accedere a queste informazioni c'è bisogno di un array ausiliario di puntatori e
quindi gli scambi avvengono su questo array di puntatori e non sull'array informazioni vero e proprio.
Accedendo all'array di puntatori è come se accedessimo all'array informazioni secondo il criterio
richiesto. */
/*IL SELECTION SORT EFFETTUA L'ORDINAMENTO DETERMINANDO A OGNI PASSO IL MINIMO O IL MASSIMO(DIPENDE
DALLA SCELTA DEL PROGRAMMATORE) DI UN SOTTOINSIEME DELL'ARRAY.
IL SELECTION SORT E':
* IN PLACE(senza l'uso di ulteriori aree di memoria)
* COMPLESSITA' DI SPAZIO = O(n) lineare
* COMPLESSITA' DI TEMPO = O((1/2)*n^2)-numero di confronti al piu' O(n)-numero di scambi */
#include<stdio.h>
#include<stdlib.h>
/*dichiarazione della struct struttura che contiene il campo info, relativo all'informazione contenuta
nell'array:
in questo caso l'informazione e' di tipo carattere riferendosi alle lettere maiuscole dell'alfabeto*/
struct struttura
{
char info;
}*pt[10];/* *pt[10] e' un array di puntatori alla struttura che e' essenziale per effettuare gli scambi
virtuali*/
typedef struct struttura STRUTTURA;/*definiamo il tipo STRUTTURA relativo alla struct struttura*/
void sel_it_vi(STRUTTURA *pt[],int n);
void sel_ri_vi(STRUTTURA *pt[],int start_i,int n);
void sel_ite_rea(STRUTTURA array[],int n);
void sel_ri_rea(STRUTTURA array[],int start_i,int n);
void main()
{
STRUTTURA array[10]={'C','E','F','G','A','B','H','I','O','Z'};/*questa e' la variabile
struttura di tipo array che contiene i 10 caratteri disordinati che vanno messi in ordine*/
pt[10]=malloc(sizeof(STRUTTURA));/*allochiamo dinamicamente la variabile che punta alla
struttura dandogli il size della struct struttura*/
int i,j,scelta;
int n=10;/*n e' il size del nostro array*/
int start_i=0;//e' una variabile che mi serve nell'implementazione dell'algoritmo nel caso
ricorsivo.start_i e' inizializzata a 0 perche' e' l'indice iniziale del nostro vettore
/*inseriamo gli elementi dell'array di struttura array nel'array di puntatori*/
for(i=0;i<10;i++)
{
pt[i]=&array[i];
}
printf("\n\nArray di partenza disordinato:\n");
//stampa dell'array disordinato
for(i=0;i<n;i++)
{
printf("%c ",array[i].info);
}
printf("\n\nMENU' SELECTION SORT");
printf("\n[1]Ordinamento con scambi reali");
printf("\n[2]Ordinamento con scambi virtuali");
printf("\n[3]Esci");
do
{
printf("\n\nScelta: ");
scanf("%d",&scelta);
- 73 -
switch(scelta)
{
case 1:
sel_ite_rea(array,n);
printf("\n\nFORMA ITERATIVA CON SCAMBI REALI:\n");
for(i=0;i<10;i++)
{
printf("%c ",array[i].info);
}
sel_ri_rea(array,start_i,n);
printf("\n\nFORMA RICORSIVA CON SCAMBI REALI:\n");
for(i=0;i<10;i++)
{
printf("%c ",array[i].info);
}
break;
case 2:
sel_it_vi(pt,n);
printf("\n\nFORMA ITERATIVA CON SCAMBI VIRTUALI:\n");
for(i=0;i<10;i++)
{
printf("%c ",pt[i]->info);
}
sel_ri_vi(pt,start_i,n);
printf("\n\nFORMA RICORSIVA CON SCAMBI VIRTUALI:\n");
for(i=0;i<10;i++)
{
printf("%c ",pt[i]->info);
}
break;
case 3:printf("\nArrivederci!");
break;
}
}while(scelta!=3);
}
/*ESERCIZIO 69
Scrivere una function C per implementare l’algoritmo Bubble Sort su un array di struttura,
sia mediante scambi reali sia mediante scambi virtuali*/
/*Definiamo il concetto di...
-scambi reali: si hanno quando i dati sono fisicamente scambiati di posto. Per scambiare l'informazione
vera e propria si richiede di scambiare non solo la chiave ma tutte le informazioni collegate a quella
chiave.
-scambi virtuali: si hanno quando i dati non sono fisicamente scambiati perché si opera tramite
puntatori: questo si fa per evitare di perdere tempo se abbiamo dati cospicui. Noi abbiamo le
informazioni ma per accedere a queste informazioni c'è bisogno di un array ausiliario di puntatori e
quindi gli scambi avvengono su questo array di puntatori e non sull'array informazioni vero e proprio.
Accedendo all'array di puntatori è come se accedessimo all'array informazioni secondo il criterio
richiesto. */
/*IL BUBBLE SORT SCORRE IL VETTORE SEQUENZIALMENTE E SCAMBIA I DUE ELEMENTI VICINI SE NON SONO
NELL'ORDINE CORRETTO. QUESTA ITERAZIONE VIENE ESEGUITA PIU' VOLTE FINO A CHE CI SONO ELEMENTI IN ORDINE
ERRATO.
IL BUBBLE SORT E':
* IN PLACE
* COMPLESSITA' DI SPAZIO = O(n) lineare
* COMPLESSITA' DI TEMPO NEL CASO PEGGIORE = O((1/2)*n^2)-confronti e O((1/2)*n^2)-scambi
* COMPLESSITA' DI TEMPO NEL CASO MEDIO = O((1/2)*n^2)-confronti e O((1/2)*n^2)-scambi */
#include<stdio.h>
#include<stdlib.h>
- 76 -
/*dichiarazione della struct struttura che contiene il campo info, relativo all'informazione contenuta
nell'array:in questo caso l'informazione e' di tipo intero riferendosi ai numeri*/
struct struttura
{
int info;
}*pt[8];/* *pt[8] e' un array di puntatori alla struttura che e' essenziale per effettuare gli scambi
virtuali*/
typedef struct struttura STRUTTURA;/*definiamo il tipo STRUTTURA relativo alla struct struttura*/
void scambi_reali(STRUTTURA array[], int n);
void scambi_virtuali(STRUTTURA *pt[], int n);
void main()
{
STRUTTURA array[8]={3,2,9,7,5,1,8,4};/*questa e' la variabile struttura di tipo array
che contiene gli 8 numeri disordinati che vanno messi in ordine*/
pt[8]=malloc(sizeof(STRUTTURA));/*allochiamo dinamicamente la variabile che punta alla
struttura dandogli il size della struct struttura*/
int i;
int n=8;//size del vettore
int scelta;
/*inseriamo gli elementi dell'array di struttura array nel'array di puntatori*/
for(i=0;i<8;i++)
{
pt[i]=&array[i];
}
//stampa dell'array disordinato
printf("\nArray di partenza disordinato:\n");
for(i=0;i<8;i++)
{
printf("%d ",array[i].info);
}
printf("\n\nMENU' BUBBLE SORT");
printf("\n[1]Ordinamento con scambi reali");
printf("\n[2]Ordinamento con scambi virtuali");
printf("\n[3]Esci dal programma");
do
{
printf("\n\nScelta: ");
scanf("%d",&scelta);
switch(scelta)
{
case 1:printf("\nORDINAMENTO CON SCAMBI REALI: \n");
scambi_reali(array,n);
for(i=0;i<8;i++)
{
printf("%d ",array[i].info);
}
break;
case 2:printf("\nORDINAMENTO CON SCAMBI VIRTUALI: \n");
scambi_virtuali(pt,n);
for(i=0;i<8;i++)
{
printf("%d ",pt[i]->info);
}
break;
case 3:printf("\nArrivederci!");
break;
}
}while(scelta!=3);
}
- 77 -
/*ESERCIZIO 70
Scrivere una function C per implementare l’algoritmo Insertion Sort su un array di struttura*/
/*L'INSERTION SORT, PER CAPIRLO, POSSIAMO IMMAGINARE DI ORDINARE IN MANIERA CRESCENTE LE CARTE DA
GIOCO. A OGNI PASSO, DOBBIAMO INSERIRE UNA NUOVA CARTA FACENDO EVENTUALMENTE SLITTARE LE CARTE FINO A
TROVARE IL POSTO GIUSTO PER LA NUOVA CARTA.
L’INSERTION SORT E’:
IN PLACE
COMPLESSITA’ DI SPAZIO = O(n) lineare
COMPLESSITA’ DI TEMPO NEL CASO PEGGIORE = O((1/2)*n^2)-confronti e O((1/2)*n^2)-scambi
COMPLESSITA’ DI TEMPO NEL CASO MIGLIORE = O(n)-confronti e O(n)-scambi*/
#include<stdio.h>
#include<stdlib.h>
struct struttura
{
int info;
};
typedef struct struttura STRUTTURA;
void main()
{
STRUTTURA array[8]={3,2,9,7,5,1,8,4};
int i,j;/*contatori di cui:
-l'indice i lo usiamo per scorrere da sinistra a destra il vettore
-l'indice j lo usiamo per confrontare a sinistra gli elementi del
vettore con la variabile el_da_ins per verificare se il valore
di quegli elementi e' maggiore*/
int el_da_ins;
int n=8;
printf("\nArray di partenza disordinato:\n");
for(i=0;i<8;i++)
{
printf("%d ",array[i].info);
}
/*il for esterno indica il numero di porzioni dell'array da ordinare in successione*/
for(i=1;i<n;i++)
{
el_da_ins=array[i].info;/*i e' l'indice dell'elemento da inserire al passo i-simo
nella corretta posizione*/
j=i-1;/*visto che devo confrontare con gli elementi di sinistra, j viene decrementato
di 1 rispetto a i*/
/*finche' non siamo arrivati all'indice iniziale dell'array e finche' l'elemento da
inserire e' minore dell'informazione corrente di indice j*/
while(j>=0&&el_da_ins<array[j].info)
{
array[j+1].info=array[j].info;/*immetti l'informazione dell'indice j-simo
successivo nell'indice precedente*/
j--;//decrementa l'indice j
}
array[j+1].info=el_da_ins;/*adesso passiamo ad inserire nella variabile temporanea
l'elemento successivo da confrontare*/
}
/*ESERCIZIO 17
Scrivere una function C per valutare un polinomio mediante l’algoritmo di Horner. Applicare l’algoritmo
ai dati dell’esempio 3 nelle dispense calcolando l’errore relativo. Usare una versione dell’algoritmo a
precisione estesa per ottenere il valore “esatto” di riferimento.*/
#include <stdio.h>
#include <math.h>
#define MAX 10
double ric_Horner(double coef[],double x, int grado,int index);
double ite_Horner(double coef[],double x, int grado);
double err_rel(double x, float flx);
void main()
{
int grado=MAX,i;//il grado massimo nel nostro caso e' 10
double coef[10];
double Err_relativo=0.0,x,risultato_Horner=0.0;
float float_risultato_Horner=0.0;
int index=0;//questa variabile e' fondamentale nel calcolo di Horner ricorsivo
risultato_Horner = ite_Horner(coef,x,grado);
printf("\nHORNER VERSIONE ITERATIVA P(%lf) = %lf",x,risultato_Horner);
risultato_Horner = ric_Horner(coef,x,grado,index);
printf("\nHORNER VERSIONE RICORSIVA P(%lf) = %lf",x,risultato_Horner);
printf("\nDouble_Horner=%22.16e\nFloat_Horner=%22.16e\n",risultato_Horner,float_risultato_Horne
r);
printf("Errore Relativo = %22.16e\n",Err_relativo);
}
- 80 -
Screenshot esercizio 17
- 81 -
/*ESERCIZIO 71
Scrivere function C per implementare l’algoritmo Merge Sort su un array in versione iterativa e
ricorsiva.*/
/*IL MERGE SORT CONFRONTA COPPIE DISGIUNTE DI ELEMENTI DEL VETTORE, CIOÈ È COME SE VEDESSE IL VETTORE
SUDDIVISO IN TANTE COPPIE: LA PRIMA-SECONDA, TERZA-QUARTA E COSÌ VIA, CHE VANNO CONFRONTATE ED
EVENTUALMENTE SCAMBIATE. LE PORZIONI ORDINATE CHE NE RISULTANO A DUE A DUE, SONO UNITE ATTRAVERSO MERGE
CON LE COPPIE ADIACENTI, FORMANDO SOTTOPORZIONI DI 4 ELEMENTI ORDINATE, E COSÌ VIA. QUESTO ALGORITMO
MERGE SORT RIAPPLICA L'ALGORITMO DI FUSIONE(MERGE) SU PORZIONI DELL'ARRAY E ALLA FINE CI TROVEREMO CON
L'INTERO VETTORE ORDINATO.
IL MERGE SORT E':
* NON IN PLACE
* COMPLESSITA' DI SPAZIO = O(n)+O(n) non lineare
* COMPLESSITA' DI TEMPO = O(n*log|2(n))-numero di confronti e O(n*log|2(n))-numero di scambi
ricordiamo che l'ordine O(n*log|2(n)) e' dato da questo prodotto:
[ Tfusione(n) x Num passi ]
O(n) x O(log|2(n)) */
#include<stdio.h>
#define N 8
void mergesort(int array[], int iniz, int fine);
void merge(int array[], int iniz, int mez, int fine);
void main()
{
int array[N]={3,2,9,7,4,1,8,5};
int n=N;//size dell'array
int iniz=0;//l'indice iniziale dell'array
int fine=N-1;//l'indice finale dell'array
int i;
printf("Array iniziale disordinato:\n");
for(i=0;i<N;i++)
{
printf("%d ",array[i]);
}
mergesort(array,0,N-1);//0 perche' e' l'inizio del vettore e N-1 la fine
printf("\n\nArray finale ordinato MERGE SORT :\n");
for(i=0;i<N;i++)
{
printf("%d ",array[i]);
}
/*MERGE SORT RICORSIVO: esso non e' in place perche' necessita di un'area di lavoro aggiuntiva detta
in questo caso la variabile b[N]*/
void mergesort(int array[], int iniz, int fine)
{
int i,j,k,m;
int mez;
if(iniz<fine)
{
mez=(iniz+fine)/2;//calcolo dell'indice elemento mediano
mergesort(array,iniz,mez);/*questa e' la chiamata relativa alla prima porzione del
vettore che va dall'inizio al mediano. Poi questa porzione viene divisa a sua volta in
due porzioni*/
mergesort(array,mez+1,fine);/*questa e' la chiamata relativa alla seconda porzione del
vettore che va dal mediano+1 alla fine. Poi questa porzione viene divisa a sua volta in
due porzioni*/
merge(array,iniz,mez,fine);/*adesso la funzione alla chiamata merge ci consente di
unire le due porzioni iniziali che hanno gli elementi ordinati tra loro ma ora gli
elementi di queste due porzioni vanno confrontati e se messi in disordine vanno prima
ordinati*/
}
else//caso banale
{
return;
}
}
- 82 -
/*ESERCIZIO 73
Scrivere una function C per implementare l’algoritmo Quick Sort.*/
/*IL QUICK SORT È UN ALGORITMO DI ORDINAMENTO RICORSIVO IN PLACE CHE, COME IL MERGE SORT, SI BASA SUL
PARADIGMA
DIVIDE ET IMPERA. LA BASE DEL SUO FUNZIONAMENTO È L'UTILIZZO RICORSIVO DELLA PROCEDURA PARTITION: PRESO
UN ELEMENTO
DA UNA STRUTTURA DATI (ES. ARRAY) SI PONGONO GLI ELEMENTI MINORI A SINISTRA RISPETTO A QUESTO E GLI
ELEMENTI MAGGIORI
A DESTRA. IL QUICKSORT, TERMINE CHE TRADOTTO LETTERALMENTE IN ITALIANO INDICA ORDINAMENTO RAPIDO, È
L'ALGORITMO DI
ORDINAMENTO CHE HA, IN GENERALE, PRESTAZIONI MIGLIORI TRA QUELLI BASATI SU CONFRONTO.
IL QUICK SORT E':
* IN PLACE
* COMPLESSITA' DI SPAZIO = O(n) lineare
* COMPLESSITA' DI TEMPO NEL CASO PEGGIORE = o(n^2)-numero di confronti
* COMPLESSITA' DI TEMPO NEL CASO MIGLIORE = o(n*log|2(n))-numero di confronti*/
#include<stdio.h>
#define N 8
int partiziona(int a[], int inizio, int fine);
void quick_sort (int a[], int inizio, int fine);
void scambia(int a[], int x, int y);
void main()
{
int a[N]={3,2,9,7,4,1,8,5};
int inizio=0;//indice iniziale del vettore
int fine=N-1;//indice finale del vettore
int i;
printf("Array iniziale disordinato:\n");
for(i=0;i<N;i++)
{
printf("%d ",a[i]);
}
quick_sort(a,0,N-1);
printf("\n\nArray finale ordinato QUICK SORT:\n");
for(i=0;i<N;i++)
{
printf("%d ",a[i]);
}
}
else//caso banale
{
return;
}
}
- 84 -
while (a[j]>=a[pivot]&&i<j)
{
j--;//passiamo all'elemento precedente da destra verso sinistra
}