Aiuto tabella hash

di il
3 risposte

Aiuto tabella hash

Salve a tutti sono nuovo del forum, ho un problema con questo codice, si tratta di una tabella hash che prende in ingresso un file di testo opportunamente formattato e crea la relativa tabella hash.
il file di testo deve essere fatto cosi:

NomePadre + (NomeMadre) # Figlio1 + Figlio2 + FiglioN
NomePadreA + (NomeMadreA) # FiglioA1 + FiglioA2 + FiglioAN

in poche parole # separa genitori da figli, + aggiunge una persona e la madre deve essere racchiusa da ()

qui di seguito vi riporto il codice, il problema è che "vengono persi" in maniera casuale alcuni genitori e alcuni figli e non capisco se il problema è in fase di parsing (tramite lo strtok) o in fase di salvataggio nella hash table o in fase di recupero hash_search...
qualcuno puo dare un'occhiata al codice testarlo e provare a trovare gli errori?
grazie infinite

CODICE:

#include <stdio.h>
#include <string.h>
#define MAXLENGTH 10000
#define SIZE 50
//struttura nodo genealogico
typedef struct node {
char nome[30]; // nome persona nodo genealogico
char consorte[30]; // nome eventuale consorte (NULL)
struct node *next; // punta al successivo nella lista fratelli
//struct gnode *prev; // punta al precedente nella lista fratelli
struct node *head; // se padre punta alla testa della lista dei figli
int figli; // numero di figli
} gnode;

//PROTOTIPI
int Hash_Insert(gnode *[], gnode *); //inserimento puntatore nodo
int Hash_Search(gnode *[], char *); //ricerca nodo
int Hash_F(int, int); //funzione hash
int ascii(char *); //ID univoco
char *strcln(char *);
gnode *newGenNode(char *); //genera nuovo nodo genealogico
void enqueue(gnode *[], gnode *); //inserimento coda
gnode *dequeue(gnode *[]); //estrazione coda
int empty(); //coda vuota
void genitore(char *, char *);
void figlio(char *, char *);
void antenato(char *, char *);
void discendente(char *, char *);

//VARIABILI GLOBALI
char buf[MAXLENGTH]; //buffer...
char *prompt = "query_>"; //prompt stampato per l'utente
char *answer = "answer_>"; //stampa per la risposta
gnode *hashTab[SIZE]; //tabella hash (puntatori nodi genealogici)
gnode *queue[SIZE]; //coda puntatori nodi genealogici
int head, tail; //coda...

//METODI E FUNZIONI
int ascii(char *a) { //converte stringa nome in ID univoco intero
int x, i, sum;
sum = 0; i = 0;

while(*(a+i) != '\0') {
x = (int) *(a+i);
sum = sum + x;
i++;
}
return sum;
}
int Hash_Insert(gnode *T[], gnode *k) {

int i, j, sum;
i = 0;
sum = ascii(k->nome); //somma caratteri ascii nome univoco
while(i < SIZE) {
j = Hash_F(sum, i);
if (*(T+j) == NULL /*|| T[j] == FLAG*/) {
*(T+j) = k;
return j;
}
else {
i++;
}
}
return -1;
}
int Hash_Search(gnode *T[], char *k) {
int i, j, sum;
gnode *tmp;
char *n;
i = 0;
sum = ascii(k); //somma caratteri ascii nome univoco
while(i < SIZE) {
j = Hash_F(sum, i);
if(*(T+j) == NULL)
break;
tmp = *(T+j);
n = tmp->nome;
if (strcmp(n, k) == 0)
return j;
i++;
}
return -1;
}
int Hash_F(int k, int i) { //Hash basato su Quadratic Probing
int a, b, j, m;
m = SIZE;
a=39;
b=276;
return (((k % m) + (a * i) + (b * i * i)) % m);
}
char *strcln(char *full) {
int i = 0;
int j = 0;
char p[200];
char *s;
while(*(full+i) != '\0') {
if(*(full+i) != ' ' && *(full+i) != '(' && *(full+i) != ')' &&
*(full+i) != ';') {
*(p+j) = *(full+i);
j++;
}
i++;
}
*(p+j) = '\0';
s=p;
return s;
}
gnode *newGenNode(char *c) {
gnode *g = (gnode *)malloc(sizeof(gnode));
strcpy(g->nome,c);
g->consorte[0] = '\0';
g->head = NULL;
g->next = NULL;
//g->prev = NULL;
g->figli = 0;
return g;
}
int empty() {
return (head == tail);
}
void enqueue(gnode *Q[], gnode *x) {
if (tail == SIZE) {
printf("Queue overflow\n");
exit(1); //coda piena
}
*(Q+tail) = x;
tail++;
}
gnode *dequeue(gnode *Q[]) {
gnode *x;
if (head == -1) {
printf("Queue underflow\n");
exit(2); //coda vuota
}
x = *(Q+head);
head++;
return x;
}
void genitore(char *x, char *y) {
return figlio(y, x);
}
void figlio(char *x, char *y) {
int j;
gnode *p, *f, *tmp;
if((j = Hash_Search(hashTab, x)) != -1) {
f = *(hashTab+j);
}
else {
printf("%s non esiste\n", x);
return;
}
if((j = Hash_Search(hashTab, y)) != -1) {
p = *(hashTab+j);
}
else {
printf("%s non esiste\n", y);
return;
}
tmp = p->head;
if(p->head == NULL) {
printf("no\n", x); //non ha figli
return;
}
for (j = 0; j < (p->figli); j++) {
if (strcmp(tmp->nome, f->nome) == 0) {
printf("sì\n");
return;
}
tmp = tmp->next; //prossimo lista fratelli
}
printf("no\n");
return;
}
void antenato(char *x, char *y) {
return discendente(y,x);
}
void discendente(char *x, char *y) {
int j, i;
gnode *v, *g, *tmp;
if((j = Hash_Search(hashTab, x)) != -1) {
g = *(hashTab+j);
}
else {
printf("%s non esiste\n", x);
return;
}
if((j = Hash_Search(hashTab, y)) != -1) {
v = *(hashTab+j);
}
else {
printf("%s non esiste\n", y);
return;
}
head = 0;
tail = 0;
if(v->head == NULL) {
printf("no\n");
return;
}
enqueue(queue, v);
while(empty() != 1) {
v = dequeue(queue);
tmp = v->head;
for(i=0; i<(v->figli); i++) {
if ((strcmp(tmp->nome, x)) == 0) {
printf("sì\n");
return;
}
enqueue(queue, tmp);
tmp = tmp->next;
}
}
printf("no\n");
return;
}
int main(int argc, char **argv[]) {
int j;
gnode *p, *c, *s, *prev;
FILE *fp;
char *str, *strToken, parents[200], progenies[200], *name;
char No[40];
int cont = 0;

//inizializzazione tabella hash
for(j=0; j<SIZE; j++)
*(hashTab+j) = NULL;
//controllo parametri
if (argc != 2) {
printf("Numero di parametri errato. USAGE: gentree FILE\n");
exit(1); //errore parametri
}
//controllo consistenza file
if ((fp = fopen(argv[1], "r")) == NULL) {
printf("Impossibile aprire il file %s\n", argv[1]);
exit(2); //errore file
}
//lettura da file e generazione nodi, albero genealogico


while((str = fgets(buf, MAXLENGTH-1, fp)) != NULL) {


prev = NULL;
strToken = strtok(str, "#"); //separa stringa genitori e figli
strcpy(parents,strToken); //stringa genitori
strToken = strtok(NULL, "#");
strcpy(progenies,strToken); //stringa figli
//NODO GENITORE (CONSORTE)
strToken = strtok(parents, "+");


name = strcln(strToken);

printf("\nPadre = _%s_",name);

j = Hash_Search(hashTab, name);

if (j == -1) {
p = newGenNode(name);
if (Hash_Insert(hashTab, p) == -1) { //inserimento hash
printf("dimensione hash insufficiente: gestire collisioni\n");
exit(3); //dimensione hash; collisioni
}
}
else {
p = *(hashTab+j); //puntatore da hash
}

strToken = strtok(NULL, "+"); //aggiorno il-la consorte (NULL)

name = strcln(strToken);

printf("\nMadre = %s",name);
strcpy(p->consorte,name);
j = Hash_Search(hashTab, name);
if (j == -1) {
c = newGenNode(name);
if (Hash_Insert(hashTab, c) == -1) { //inserimento hash
printf("dimensione hash insufficiente: gestire collisioni\n");
exit(3); //dimensione hash; collisioni
}
}
else {
c = *(hashTab+j); //puntatore da hash
}
strcpy(c->consorte,p->nome);




//NODI FIGLI
strToken = strtok(progenies, "+");
int i = 0;
while (strToken != NULL) {
//creo un nuovo nodo figlio
p->figli++; c->figli++; //aggiorno numero figli
name = strcln(strToken);


printf("\nFigli = %s",name);
s = newGenNode(name);


if (p->head == NULL) { //padre -> primo figlio
p->head = s;
c->head = s;
}
if (Hash_Insert(hashTab, s) == -1) {//inserimento hash
printf("dimensione hash insufficiente: gestirecollisioni\n");
exit(3); //dimensione hash; risolvere collisioni
}
if(prev != NULL)
prev->next = s;
prev = s;
strToken = strtok(NULL, "+");
} //end while
s->next = p->head; //chiusura lista fratelli


printf("\n-----------------------");


}//end while

fclose(fp); //chiusura file genealogico sorgente
//ciclo di ascolto comandi utenti


char com[100], x[100], y[100], g;
while(1) {
printf("%s", prompt);
int k;
int input = 0;
for(j=0; j<100; j++) { // pulizia buffers
com[j]='\0';
x[j]='\0';
y[j]='\0';
}
k = 0;

while((g=getchar()) != '\n') {
if(k == 100) {
printf("buffer comando overflow");
break;
}
if(input == 0) {
if(g == '(') {
input++;
k = 0;
} else {
com[k++] = g;
}
} else {
if(input == 1) {
if(g == ',') {
input++;
k = 0;
} else {
x[k++] = g;
}
} else {
if(input == 2) {
if(g == ')') {
input++;
k = 0;
} else {
y[k++] = g;
}
}
}
}
}

//j = Hash_Search(hashTab, x);
//printf("\n 1 _ %d",j);

/*if (j == -1) {
p = newGenNode(x);
if (Hash_Insert(hashTab, p) == -1) { //inserimento hash
printf("dimensione hash insufficiente: gestire collisioni\n");
exit(3); //dimensione hash; collisioni
}
}
else {
p = *(hashTab+j); //puntatore da hash
}

int hg = Hash_Search(hashTab, x);
printf("[%s]",p->nome);
*/

if(!strcmp(com, "exit"))
exit(0);
if(!strcmp(com, "figlio")) {
figlio(x, y);
}
else {
if(!strcmp(com, "discendente")) {
discendente(x,y);
}
else {
if(!strcmp(com, "genitore")) {
genitore(x,y);
}
else {
if(!strcmp(com, "antenato")) {
antenato(x,y);
}
else {
if(!strcmp(com, "help")) {
printf("uso del comando:\n\tfiglio x y\n\tgenitore x y");
printf(" \n\tdiscendente x y\n\tantenato x y\n");
}
else {
printf("comando non conosciuto\ndigitare help\n");
}
}
}
}
}
}
return;
}

3 Risposte

  • Re: Aiuto tabella hash

    Ti posto il tuo codice ritoccato e funzionante; dove ho aggiustato è commentato; principalmente la funzione che dava problemi era la strcln: sia nella fase iniziale, quando stampavi a video le varie famiglie, sia quando poi andavi a memorizzare nella tavola i nomi (manteneva per alcuni nomi, in particolare gli ultimi figli di ogni famiglia anche il carattere 'a capo', perchè sono l'ultimo elemento della riga del file, così poi quando usavi la ascii, questa dava risultati diversi per uno stesso nome, in quanto quello che prendeva da console, dalle funzioni figlio o genitore, non conteneva il carattere a capo, mentre quello presente nella tavola si); le altre cose che ho toccato sono minime, ma comunque commentate.

    spero di essere stato chiaro
    
    #include <stdio.h>
    #include <string.h>
    #define MAXLENGTH 10000
    #define SIZE 50
    //struttura nodo genealogico
    typedef struct node {
    	char nome[30]; // nome persona nodo genealogico
    	char consorte[30]; // nome eventuale consorte (NULL)
    	struct node *next; // punta al successivo nella lista fratelli
    	//struct gnode *prev; // punta al precedente nella lista fratelli
    	struct node *head; // se padre punta alla testa della lista dei figli
    	int figli; // numero di figli
    } gnode;
    
    //PROTOTIPI
    int Hash_Insert(gnode *[], gnode *); //inserimento puntatore nodo
    int Hash_Search(gnode *[], char *); //ricerca nodo
    int Hash_F(int, int); //funzione hash
    int ascii(char *); //ID univoco
    char *strcln(char *);
    gnode *newGenNode(char *); //genera nuovo nodo genealogico
    void enqueue(gnode *[], gnode *); //inserimento coda
    gnode *dequeue(gnode *[]); //estrazione coda
    int empty(); //coda vuota
    void genitore(char *, char *);
    void figlio(char *, char *);
    void antenato(char *, char *);
    void discendente(char *, char *);
    
    //VARIABILI GLOBALI
    char buf[MAXLENGTH]; //buffer...
    char *prompt = "query_>"; //prompt stampato per l'utente
    char *answer = "answer_>"; //stampa per la risposta
    gnode *hashTab[SIZE]; //tabella hash (puntatori nodi genealogici)
    gnode *queue[SIZE]; //coda puntatori nodi genealogici
    int head, tail; //coda...
    
    //METODI E FUNZIONI
    int ascii(char *a) { //converte stringa nome in ID univoco intero
    	int x, i, sum;
    	sum = 0; i = 0;
    	
    	while(*(a+i) != '\0') {
    	x = (int) *(a+i);
    	sum = sum + x;
    	i++;
    	}
    	return sum;
    }
    
    int Hash_Insert(gnode *T[], gnode *k) {
    
    	int i, j, sum;
    	i = 0;
    	sum = ascii(k->nome); //somma caratteri ascii nome univoco
    	while(i < SIZE) {
    		j = Hash_F(sum, i);
    		if (*(T+j) == NULL /*|| T[j] == FLAG*/) {
    			*(T+j) = k;
    			return j;
    		}
    		else {
    			i++;
    		}
    	}
    	return -1;
    }
    
    int Hash_Search(gnode *T[], char *k) {
    	int i, j, sum;
    	gnode *tmp;
    	char *n;
    	i = 0;
    	sum = ascii(k); //somma caratteri ascii nome univoco
    	while(i < SIZE) {
    		j = Hash_F(sum, i);
    		if(*(T+j) == NULL)
    			break;
    		tmp = *(T+j);
    		n = tmp->nome;
    		if (strcmp(n, k) == 0)
    			return j;
    		i++;
    	}
    	return -1;
    }
    
    int Hash_F(int k, int i) { //Hash basato su Quadratic Probing
    	int a, b, j, m;
    	m = SIZE;
    	a=39;
    	b=276;
    	return (((k % m) + (a * i) + (b * i * i)) % m);
    }
    
    char *strcln(char *full) {
    	int i = 0;
    	int j = 0;
    	char* p = (char*)malloc(sizeof(char)*200);//come avevi messo te prima non funzionava bene, certe volte restituiva una stringa vuota
    	while(*(full+i) != '\0') {
    		if(*(full+i) != ' ' && *(full+i) != '(' && *(full+i) != ')' && *(full+i) != ';' && *(full+i) != '\n') {//ho aggiunto questa ultima condizione perchè manteneva per alcuni nomi anche il carattera 'a capo', dando poi problemi nella funzione ascii
    		*(p+j) = *(full+i);									
    		j++;																									
    		}																										
    		i++;
    	}
    	*(p+j) = '\0';
    	return p;
    }
    
    gnode *newGenNode(char *c) {
    	gnode *g = (gnode *)malloc(sizeof(gnode));
    	strcpy(g->nome,c);
    	g->consorte[0] = '\0';
    	g->head = NULL;
    	g->next = NULL;
    	//g->prev = NULL;
    	g->figli = 0;
    	return g;
    }
    
    int empty() {
    	return (head == tail);
    }
    
    void enqueue(gnode *Q[], gnode *x) {
    	if (tail == SIZE) {
    	printf("Queue overflow\n");
    	exit(1); //coda piena
    	}
    	*(Q+tail) = x;
    	tail++;
    	}
    	gnode *dequeue(gnode *Q[]) {
    	gnode *x;
    	if (head == -1) {
    	printf("Queue underflow\n");
    	exit(2); //coda vuota
    	}
    	x = *(Q+head);
    	head++;
    	return x;
    }
    
    void genitore(char *x, char *y) {
    	return figlio(y, x);
    }
    
    void figlio(char *x, char *y) {
    	int j;
    	gnode *p, *f, *tmp;
    	if((j = Hash_Search(hashTab, x)) != -1) {
    		f = *(hashTab+j);
    	}
    	else {
    		printf("%s non esiste\n", x);
    		return;
    	}
    	
    	if((j = Hash_Search(hashTab, y)) != -1) {
    		p = *(hashTab+j);
    	}
    	else {
    		printf("%s non esiste\n", y);
    		return;
    	}
    	tmp = p->head;
    	if(p->head == NULL) {
    	printf("no\n", x); //non ha figli
    	return;
    	}
    	for (j = 0; j < (p->figli); j++) {
    	if (strcmp(tmp->nome, f->nome) == 0) {
    	printf("si\n");
    	return;
    	}
    	tmp = tmp->next; //prossimo lista fratelli
    	}
    	printf("no\n");
    	return;
    }
    
    void antenato(char *x, char *y) {
    	return discendente(y,x);
    }
    
    void discendente(char *x, char *y) {
    	int j, i;
    	gnode *v, *g, *tmp;
    	if((j = Hash_Search(hashTab, x)) != -1) {
    	g = *(hashTab+j);
    	}
    	else {
    	printf("%s non esiste\n", x);
    	return;
    	}
    	if((j = Hash_Search(hashTab, y)) != -1) {
    	v = *(hashTab+j);
    	}
    	else {
    	printf("%s non esiste\n", y);
    	return;
    	}
    	head = 0;
    	tail = 0;
    	if(v->head == NULL) {
    	printf("no\n");
    	return;
    	}
    	enqueue(queue, v);
    	while(empty() != 1) {
    	v = dequeue(queue);
    	tmp = v->head;
    	for(i=0; i<(v->figli); i++) {
    	if ((strcmp(tmp->nome, x)) == 0) {
    	printf("si\n");
    	return;
    	}
    	enqueue(queue, tmp);
    	tmp = tmp->next;
    	}
    	}
    	printf("no\n");
    	return;
    }
    
    
    int main(int argc, char *argv[]) {//avevi messo come secondo argomento un array di puntatori a stringhe, invece devi mettere una array a puntatori di char, quindi un'array di stringhe
    	int j;
    	gnode *p, *c, *s, *prev;
    	FILE *fp;
    	char *str, *strToken, parents[200], progenies[200], *name;
    	char No[40];
    	int cont = 0;
    
    	//inizializzazione tabella hash
    	for(j=0; j<SIZE; j++)
    	*(hashTab+j) = NULL;
    	//controllo parametri
    	if (argc != 2) {
    	printf("Numero di parametri errato. USAGE: gentree FILE\n");
    	exit(1); //errore parametri
    	}
    	//controllo consistenza file
    	if ((fp = fopen(argv[1], "r")) == NULL) {
    	printf("Impossibile aprire il file %s\n", argv[1]);
    	exit(2); //errore file
    	}
    	//lettura da file e generazione nodi, albero genealogico
    
    	
    	while((str = fgets(buf, MAXLENGTH-1, fp)) != NULL) {
    
    	
    	prev = NULL;
    	strToken = strtok(str, "#"); //separa stringa genitori e figli
    	strcpy(parents,strToken); //stringa genitori
    	strToken = strtok(NULL, "#");
    	strcpy(progenies,strToken); //stringa figli
    	//NODO GENITORE (CONSORTE)
    	strToken = strtok(parents, "+");
    
    	name = strcln(strToken);
    	
    	printf("\nPadre = _%s_",name);
    
    	j = Hash_Search(hashTab, name);
    
    	if (j == -1) {
    		p = newGenNode(name);
    		if (Hash_Insert(hashTab, p) == -1) { //inserimento hash
    		printf("dimensione hash insufficiente: gestire collisioni\n");
    		exit(3); //dimensione hash; collisioni
    		}
    	}
    	else {
    		p = *(hashTab+j); //puntatore da hash
    	}
    
    	strToken = strtok(NULL, "+"); //aggiorno il-la consorte (NULL)
    
    	name = strcln(strToken);
    
    	printf("\nMadre = %s",name);
    	strcpy(p->consorte,name);
    	j = Hash_Search(hashTab, name);
    	if (j == -1) {
    	c = newGenNode(name);
    	if (Hash_Insert(hashTab, c) == -1) { //inserimento hash
    	printf("dimensione hash insufficiente: gestire collisioni\n");
    	exit(3); //dimensione hash; collisioni
    	}
    	}
    	else {
    	c = *(hashTab+j); //puntatore da hash
    	}
    	strcpy(c->consorte,p->nome);
    
    	//NODI FIGLI
    	strToken = strtok(progenies, "+");
    	int i = 0;
    	while (strToken != NULL) {
    	//creo un nuovo nodo figlio
    	p->figli++; c->figli++; //aggiorno numero figli
    	name = strcln(strToken);
    
    
    	printf("\nFigli = %s",name);
    	s = newGenNode(name);
    
    
    	if (p->head == NULL) { //padre -> primo figlio
    		p->head = s;
    		c->head = s;
    	}
    	if (Hash_Insert(hashTab, s) == -1) {//inserimento hash
    	printf("dimensione hash insufficiente: gestirecollisioni\n");
    	exit(3); //dimensione hash; risolvere collisioni
    	}
    	if(prev != NULL)
    		prev->next = s;
    		
    	prev = s;
    	strToken = strtok(NULL, "+");
    	} //end while
    	s->next = p->head; //chiusura lista fratelli
    
    
    	printf("\n-----------------------\n");
    
    
    	}//end while
    
    	fclose(fp); //chiusura file genealogico sorgente
    	//ciclo di ascolto comandi utenti
    
    
    	char* com = (char*)malloc(sizeof(char)*100);
    	char* x = (char*)malloc(sizeof(char)*100);
    	char* y = (char*)malloc(sizeof(char)*100);
    	char g;
    	
    	while(1) {
    	printf("%s", prompt);
    	int k;
    	int input = 0;
    	
    	for(j=0; j<100; j++) { // pulizia buffers
    		com[j]='\0';
    		x[j]='\0';
    		y[j]='\0';
    	}
    	
    	k = 0;
    
    	while((g=getchar()) != '\n') {
    		if(k == 100) {
    			printf("buffer comando overflow");
    			break;
    		}
    		if(input == 0) {
    			if(g == '(') {
    				input++;
    				k = 0;
    			} else {
    				com[k++] = g;
    			}
    		} 
    		else {
    			if(input == 1) {
    				if(g == ',') {
    					input++;
    					k = 0;
    				} else {
    					x[k++] = g;
    				}
    			} 
    			else {
    				if(input == 2) {
    					if(g == ')') {
    						input++;
    						k = 0;
    					} 
    					else {
    					y[k++] = g;
    					}
    				}
    			}
    		}
    	}
    
    	
    	if(!strcmp(com, "exit"))
    	exit(0);
    	
    	//ti ho sistemato la scelta multipla, prima era molto incasinata
    	if(!strcmp(com, "figlio")) {
    		figlio(x,y);
    	}
    	else if(!strcmp(com, "discendente")) {
    		discendente(x,y);
    	}
    	else if(!strcmp(com, "genitore")) {
    		genitore(x,y);
    	}
    	else if(!strcmp(com, "antenato")) {
    		antenato(x,y);
    	}
    	else if(!strcmp(com, "help")) {
    		printf("uso del comando:\n\tfiglio x y\n\tgenitore x y");
    		printf(" \n\tdiscendente x y\n\tantenato x y\n");
    	}
    	else {
    		printf("comando non conosciuto\ndigitare help\n");
    	}
    	}//end while(1)
    	return; 
    }
    
    [/code]
  • Re: Aiuto tabella hash

    Grazie infinite!! senza di te non avrei saputo come risolvere
  • Re: Aiuto tabella hash

    Di niente, alla prossima
Devi accedere o registrarti per scrivere nel forum
3 risposte