tablica haszująca

0

Siema, napisałem już cały kod tablicy haszowej, której elementy mają ciągnąć za sobą łańcuszek jakichś liczb. Niestety przy próbie dodania elementu program pada. Domyślam się, że jest problem z jakimś wskaźnikiem, pewnie mam złą wizję zadania...

 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TRUE 1
#define FALSE 0

struct element
{
	unsigned int value;
	struct element *next;
};

struct element *tablica[100];

void dopisz();
void usun();
void show();
int add(int value);
int del(int value);


int main()
{

	int isEnd = FALSE;
	char answer[100];
	while(!isEnd)
	{
		printf("\n\t\tMENU\n");
		printf("[0] - zakonczenie programu\n");
                printf("[1] - dopisanie wyrazu\n");
                printf("[2] - usuniecie wyrazu\n");
                printf("[3] - wyswietlenie zawartosci listy\n");
		scanf("%s", answer);

                switch(answer[0])
                {
                        case '0':
                                printf("Zakonczenie!\n");
                                isEnd=TRUE;
                                break;
                        case '1':
                                dopisz();
                                break;
                        case '2':
                                usun();
                                break;
                        case '3':
                                show();
                                break;
                        default:
                                printf("Bledny wybor...\n");
                }
	}
	return 0;
}

void dopisz()
{
    	int value;

    	printf("Wartosc: ");
    	scanf("%d", &value);
   	if(add(value))
		printf("\nDodano\n");
	else
		printf("\nPodany element juz istnieje!\n");
}

void usun()
{
	int value;
    	printf("\nJaki element usunac?\t");
    	scanf("%d", &value);
    	if(del(value))
        	printf("\nUsunieto.\n");
    	else
        	printf("\nPodany wyraz nie istnieje.\n");

}

void show()
{
    int i;
	struct element *temp;
	for(i=0; i<100; i++)
	{
		if(tablica[i]->next == NULL)
			continue;
		else
		{
			printf("#%d:\t", i);
			for(temp=tablica[i]->next; temp!=NULL; temp=temp->next)
			{
				printf("%d\t", temp->value);
			}
			putchar('\n');
		}
	}
}

int add(int value)
{
    int index = value%100;
    struct element *curr, *tmp, *newItem;

    newItem = (struct element*)malloc(sizeof(struct element));
    if(newItem == NULL)
    {
        printf("\nBrak pamieci!\n");
        return FALSE;
    }

    newItem->value = value;
    newItem->next = NULL;

    if(tablica[index]->next == NULL)
    {
        tablica[index]->next = newItem;
        return TRUE;
    }
    for(curr=tablica[index]; curr!=NULL; curr=curr->next)
    {
        if(curr->value == value)
            return FALSE;
    }

    tmp = tablica[index]->next;
    tablica[index]->next = newItem;
    newItem->next = tmp;

    return TRUE;
}
int del(int value)
{
    int index = value%100;
    struct element *curr, *prev;

    if(tablica[index] == NULL)
    {
        return FALSE;
    }

    for(curr=tablica[index]; curr!=NULL; curr=curr->next)
    {
        if(curr->value == value)
        {
            prev->next = curr->next;
            free(curr);
            return TRUE;
        }
        prev = curr;
    }

    return FALSE;
}

0

*tablica. Gdzie ją alokujesz?

0

Czyli w mainie pętlę i przelecieć całą tablicę mallockiem? Pytałem się prowadzącego laboratoria czy tak trzeba zrobić, twierdził, że nie.

0

masz coś takiego
struct element *tablica[100];
powiedz mi, na co pokazuję *tablica? Na coś konkretnego czy na coś losowego? Masz zarezerwowaną pamieć dla tego wskaźnika?

0

No tak, ahh.
Nic nie przypisałem dla tego wskaźnika, a chcę coś na nim robić.
Mogłoby być np:
tablica = (struct element*)malloc(sizeof(100*(struct element)));
?

Dobra, sprawdziłem i tak nie pasuje. Ale czuję już problem. :P

Bardziej pasowałoby mi coś takiego:
tablica = (struct element*)malloc(sizeof (struct element)*100);
Ale też nie przechodzi. No ale jest tylko jeden error, że źle przypisuję tę pamięć.

Zrobiłem teraz tak:
struct element tablica;
tablica = (struct element
)malloc(sizeof (struct element)*100);
Kompiluje się bez problemu, ale niestety nie działa. Czy muszę przypisać nulle do wskaźników?

1

Problem jest taki, że niepoprawnie obsługujesz operacje na listach jednokierunkowych. Np. tu:

    if(tablica[index]->next == NULL)
    {
        tablica[index]->next = newItem;
        return TRUE;
    }
    for(curr=tablica[index]; curr!=NULL; curr=curr->next)
    {
        if(curr->value == value)
            return FALSE;
    }
 
    tmp = tablica[index]->next;
    tablica[index]->next = newItem;
    newItem->next = tmp;

Kompletnie źle. Co gdy lista będzie pusta (tablica[index] == NULL)? Program się wywali, bo pierwsze co robisz to if(tablica[index]->next == NULL). Później jest jeszcze gorzej, złe wskaźniki w nie te miejsca co trzeba. Musisz poprawić operacje na listach jednokierunkowych.

Sama tablica wskaźników jest OK. Pamięć ma zadeklarowaną bo jest to tablica statyczna i do tego globalna. Problem jest z samymi wskaźnikami i jak z nich korzystasz.

0

Tu mam pytanie, tablica[index] powinna być zawsze null i dopiero wskaźnik do jej następnego elementu ma być zajmowany?

0

Moja rada, stwórz sobie funkcje do obsługi listy niezależne od hash table:

int dodajWartosc(element **root, int value);
int usunWartosc(element **root, int value);
int drukujListe(element *root);

Wtedy wszystkie operacje na hash table znacznie sie uproszczą i będą czytelniejsze (obsługa listy też będzie czytelniejsza).

0

A czy wtedy globalność tablicy nie straci sensu? Po to mam ją zrobić globalną, by właśnie nie przesyłać jej w funkcje.

0

zrób sobie coś takiego:

struct Wartosc
{
  int cos1;
  int cos2;
};

struct Element
{
  struct Element* next;
  struct Wartosc w;
};

struct Elementy
{
  struct Element* root;
};

struct TablicaHashowa
{
  struct Elementy[/*jakas wartosc*/] elementy;
};

int porownajElementy(struct Element* a, struct Element* b)
{ /* ... */ }

int policzHashElementu(struct Element* a)
{ /* ... */ }

void dodajElementDoListy(struct Elementy* lista, struct Element* element)
{ /* ... */ }

void dodajElementDoTablicyHashowej(struct TablicaHashowa* h, struct Element* element)
{
  dodajElementDoListy(h->elementy[policzHashElementu(element)], element);
}

struct TablicaHashowa* stworzTabliceHashowa()
{ /* ... */ }

void usunTabliceHashowa(struct TablicaHashowa* h)
{ /* ... */ }
4

W C można robić takie oto sztuczki:

struct TablicaHashowa
  {
   unsigned ile;
   struct Elementy[1] elementy;
  };
 
struct TablicaHashowa *stworzTabliceHashowa(unsigned ile)
  {
   struct TablicaHashowa *T;
   T=(struct TablicaHashowa *)malloc(sizeof(TablicaHashowa)+(ile-1)*sizeof(struct Elementy));
   T->ile=ile;
   ...
   return T;
  }

Więc mamy jednolity obszar w TablicaHashowa w której składowa elementy to tablica, która może być większa niż to zadeklarowane w strukturze.

1
mba napisał(a):

Tu mam pytanie, tablica[index] powinna być zawsze null i dopiero wskaźnik do jej następnego elementu ma być zajmowany?
tablica[index] to lista - wskaźnik do jej pierwszego elementu. Jeśli lista jest pusta wskaźnik ten będzie NULL. Jeśli lista zawiera jeden element, wskaźnik ten będzie wskazywać na ów element, a wskaźnik do jego następnika będzie NULL. Dopiero przy dwóch elementach będziemy mieli niezerowy wskaźnik do następnika - od pierwszego do drugiego elementu.

0

Niestety nie mogę zmieniać struktury, musi zostać taka jaka jest. Tablica ma być deklarowana jako globalna.
@adf88, dziękuję, rozjaśniło mi to trochę sytuację.

Mam jeszcze pytanie. Czemu ta pętla jest zła?
for(curr=tablica[index]; curr!=NULL; curr=curr->next)
{
if(curr->value == value)
return FALSE;
}
Program ma działać tak, że nie można dwa razy dodać tej samej wartości. A tutaj jest pętla, która dla danego indeksu sprawdza wszystkie wartości i jeżeli trafi na powtórkę, to od razu zwraca komunikat o błędzie.

0
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TRUE 1
#define FALSE 0

struct element
{
	unsigned int value;
	struct element *next;
};

struct element *tablica[100];

void dopisz();
void usun();
void show();
int add(int value);
int del(int value);


int main()
{


	int isEnd = FALSE;
	char answer[100];
	while(!isEnd)
	{
		printf("\n\t\tMENU\n");
		printf("[0] - zakonczenie programu\n");
                printf("[1] - dopisanie wyrazu\n");
                printf("[2] - usuniecie wyrazu\n");
                printf("[3] - wyswietlenie zawartosci listy\n");
		scanf("%s", answer);

                switch(answer[0])
                {
                        case '0':
                                printf("Zakonczenie!\n");
                                isEnd=TRUE;
                                break;
                        case '1':
                                dopisz();
                                break;
                        case '2':
                                usun();
                                break;
                        case '3':
                                show();
                                break;
                        default:
                                printf("Bledny wybor...\n");
                }
	}
	return 0;
}

void dopisz()
{
    	int value;

    	printf("Wartosc: ");
    	scanf("%d", &value);
   	if(add(value))
		printf("\nDodano\n");
	else
		printf("\nPodany element juz istnieje!\n");
}

void usun()
{
	int value;
    	printf("\nJaki element usunac?\t");
    	scanf("%d", &value);
    	if(del(value))
        	printf("\nUsunieto.\n");
    	else
        	printf("\nPodany wyraz nie istnieje.\n");

}

void show()
{
    int i;
	struct element *temp;
	for(i=0; i<100; i++)
	{
		if(tablica[i] == NULL)
			continue;
		else
		{
			printf("#%d:\t", i);
			for(temp=tablica[i]; temp!=NULL; temp=temp->next)
			{
				printf("%d\t", temp->value);
			}
			putchar('\n');
		}
	}
}

int add(int value)
{
    int index = value%100;
    struct element *curr, *prev, *newItem;

    newItem = (struct element*)malloc(sizeof(struct element));
    if(newItem == NULL)
    {
        printf("\nBrak pamieci!\n");
        return FALSE;
    }

    newItem->value = value;
    newItem->next = NULL;

    if(tablica[index] == NULL)
    {
        tablica[index] = newItem;
        return TRUE;
    }
    for(curr=tablica[index]; curr!=NULL; curr=curr->next)
    {
        if(curr->value == value)
            return FALSE;
        prev = curr;
    }
    prev->next = newItem;

    return TRUE;
}
int del(int value)
{
    int index = value%100;
    struct element *curr, *prev;

    if(tablica[index] == NULL)
    {
        return FALSE;
    }
    if(tablica[index]->value == value)
    {
        tablica[index] = NULL;
        return TRUE;
    }

    for(curr=tablica[index]; curr!=NULL; curr=curr->next)
    {
        if(curr->value == value)
        {
            prev->next = curr->next;
            free(curr);
            return TRUE;
        }
        prev = curr;
    }

    return FALSE;
}

Poprawiłem kod i wydaje się wszystko działać. Dzięki wielkie @adf88 za wskazówki. :) Czy jeszcze coś należałoby zmienić?

1

Dalej jest źle dodawanie do listy. Usuwanie też.

0

Weź wykorzystaj takie coś do obsługi list:

int listAdd(element **root, int value) {
    struct element* newItem = (struct element*)malloc(sizeof(struct element));
    if (newItem) {
        newItem->value=value;
        newItem->next = *root;
        *root = newItem;
        return TRUE;
    }
    return FALSE;
}

int listHasValue(element *root, int value) {
    for(struct element* item = root; item; item = item->next) {
        if (item->value = value)
            return TRUE;
    }
    return FALSE;
}

i korzystając z tego zbuduj obsługę hash table.

0

Ok, faktycznie źle działa. Zaraz wezmę się do poprawy. Zamiast usunąć jeden element, usuwa wszystkie.

1

Proponuję weź ten program co pisałeś ostatnio (lista jednokierunkowa) i przerób go tak, aby działał nie tylko na konkretnej liście globalnej, a na dowolnej liście. Wtedy możesz ten kod przenieść do tego programu tutaj.

Już wiesz czemu zmienne globalne są złe? ;)

0

Na razie staram się poprawić ten aktualny kod, zanim wezmę się za taką globalną przeróbkę. Poprawiłem usuwanie, wszystko działa. Co jest nie tak z dodawaniem? Nie mogę znaleźć błędu.
Pomysł modyfikacji listy jednokierunkowej jest fajny.
Co do zmiennych globalnych - narzucono mi taką formę tablicy wskaźników, niestety nie mogę nic z tym zrobić. Zmienne globalne czasem ułatwiają sprawę, w tym przypadku dość mocno mnie pogubiły. :) Ale chyba globalność tej tablicy wskaźników nie działa jakoś na moją niekorzyść w tym programie?

1

W tym programie nie, ale zaraz może się okazać, że kolejnym krokiem będzie użycie dwóch hashmap i będziesz znowu w dupie. W każdym razie nic nie stoi na przeszkodzie, aby ta hasmapa z której korzystasz w tym programie była globalna, ale funkcje na niej operujące były już generyczne (działały na dowolnej hashmapie przekazanej przez parametr).

Naprawdę nalegam abyś przerobił swój poprzedni program. Niech sama lista będzie globalne, ale funkcje niech operują na dowolnej liście. Tak się to powinno robić. Tworząc kod trzeba myśleć o jego ponownym wykorzystaniu czy wprowadzaniu modyfikacji. Gdybyś od początku robił jak należy tego wątku by nie było wcale.

Na zachętę przerobię dodawanie:

void add(element **list, char *bufor) // podwójny wskaźnik (adres na wskaźnik przechowujący głowę listy) aby móc zmodyfikować głowę listy
{
    struct element *curr, *prev = NULL;
    int cmp = - 1;
    for(curr=*list; curr!=NULL; curr=curr->next) // w tej linijce zmiana - "*list" zamiast "head"
    {
        cmp = strcmp(curr->word, bufor);
        if (cmp >= 0) break;
        prev = curr;
    }
 
    if (cmp == 0)
    {
        curr->arity++;
    }
    else
    {
        struct element *new = (struct element*)malloc(sizeof(struct element));
        if(new == NULL)
        {
            printf("Brak pamieci\n");
            return;
        }
        new->word = strdup(bufor);
        new->arity=1;
        new->next=curr;
        if (prev!=NULL)
        {
            prev->next = new;
        }
        else
        {
            *list = new; // w tej linijce zmiana - "*list" zamiast "head"
        }
    }
}

void dopisz()
{
    char bufor[31];
 
    printf("Wyraz: ");
        scanf("%s", bufor);
        add(&head, bufor); // w tej linijce zmiana, przekazujemy dodatkowy parametr, adres wskaźnika głowy listy
}

Jak widzisz, przeróbki są niewielkie.

0

Rozumiem, przerobię jutro tamten program. Chciałem tylko wiedzieć co z tą globalną listą. ;) No i nie chciałem zostawić bez dokończenia czegoś, co już zacząłem.

0

Napisałem to w ten sposób:

 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TRUE 1
#define FALSE 0

struct element
{
	unsigned int value;
	struct element *next;
};

struct element *tablica[100];

void dopisz();
void usun();
void show();
int del(int value, struct element **root);
int add(int value, struct element **root);
void print(struct element **root, int numer);

int main()
{


	int isEnd = FALSE;
	char answer[100];
	while(!isEnd)
	{
		printf("\n\t\tMENU\n");
		printf("[0] - zakonczenie programu\n");
                printf("[1] - dopisanie wyrazu\n");
                printf("[2] - usuniecie wyrazu\n");
                printf("[3] - wyswietlenie zawartosci listy\n");
		scanf("%s", answer);

                switch(answer[0])
                {
                        case '0':
                                printf("Zakonczenie!\n");
                                isEnd=TRUE;
                                break;
                        case '1':
                                dopisz();
                                break;
                        case '2':
                                usun();
                                break;
                        case '3':
                                show();
                                break;
                        default:
                                printf("Bledny wybor...\n");
                }
	}
	return 0;
}


void dopisz()
{
    	int value;
    	printf("Wartosc: ");
    	scanf("%d", &value);
   	if(add(value, &tablica[value%100]))
		printf("\nDodano\n");
	else
		printf("\nPodany element juz istnieje!\n");
}

void usun()
{
	int value;
    	printf("\nJaki element usunac?\t");
    	scanf("%d", &value);
    	if(del(value, &tablica[value%100]))
        	printf("\nUsunieto.\n");
    	else
        	printf("\nPodany wyraz nie istnieje.\n");

}

int add(int value, struct element **root)
{
    struct element *curr, *prev, *newItem;

    newItem = (struct element*)malloc(sizeof(struct element));
    if(newItem == NULL)
    {
        printf("\nBrak pamieci!\n");
        return FALSE;
    }

    newItem->value = value;
    newItem->next = NULL;

    if(*root == NULL)
    {
        *root = newItem;
        return TRUE;
    }
    for(curr=*root; curr; curr=curr->next)
    {
        if(curr->value == value)
            return FALSE;
        prev = curr;
    }
    prev->next = newItem;

    return TRUE;
}



int del(int value, struct element **root)
{
    int index = value%100;
    struct element *curr, *prev;

    if(*root == NULL)
    {
        return FALSE;
    }
    if((*root)->value == value)
    {
        if((*root)->next != NULL)
        {
            *root = tablica[index]->next;
            return TRUE;
        }
        *root = NULL;
        return TRUE;
    }

    for(curr=*root; curr!=NULL; curr=curr->next)
    {
        if(curr->value == value)
        {
            prev->next = curr->next;
            free(curr);
            return TRUE;
        }
        prev = curr;
    }

    return FALSE;
}


void show()
{
    int i;

	for(i=0; i<100; i++)
	{
		print(&tablica[i], i);
	}
}

void print(struct element **root, int numer)
{
    struct element *temp;
    if(*root == NULL)
			return;
    else
    {
        printf("#%d:\t", numer);
        for(temp=*root; temp!=NULL; temp=temp->next)
        {
            printf("%d\t", temp->value);
        }
        putchar('\n');
    }
}

Skorzystałem z funkcji z tego nowszego programu, bo tamte sortowały i w ogóle operowały na napisach. Czy coś jeszcze należałoby zmienić?

1 użytkowników online, w tym zalogowanych: 0, gości: 1