Jedną z rzeczy, których brakuje mi podczas pisania programów w C, jest struktura danych słownikowych. Jaki jest najwygodniejszy sposób zaimplementowania go w C? Nie szukam wydajności, ale łatwości kodowania od podstaw. Nie chcę też, żeby był ogólny - wystarczy coś takiego jak string-> int. Ale chcę, aby można było przechowywać dowolną liczbę elementów.
Ma to bardziej służyć jako ćwiczenie. Wiem, że są dostępne biblioteki innych firm, z których można korzystać. Ale pomyśl przez chwilę, że nie istnieją. W takiej sytuacji najszybciej można zaimplementować słownik spełniający powyższe wymagania.
c
data-structures
dictionary
Rohit
źródło
źródło
Odpowiedzi:
Sekcja 6.6 języka programowania C przedstawia prostą słownikową (z możliwością hashy) strukturę danych. Nie sądzę, żeby użyteczna implementacja słownika mogła być prostsza niż ta. Dla Twojej wygody reprodukuję tutaj kod.
struct nlist { /* table entry: */ struct nlist *next; /* next entry in chain */ char *name; /* defined name */ char *defn; /* replacement text */ }; #define HASHSIZE 101 static struct nlist *hashtab[HASHSIZE]; /* pointer table */ /* hash: form hash value for string s */ unsigned hash(char *s) { unsigned hashval; for (hashval = 0; *s != '\0'; s++) hashval = *s + 31 * hashval; return hashval % HASHSIZE; } /* lookup: look for s in hashtab */ struct nlist *lookup(char *s) { struct nlist *np; for (np = hashtab[hash(s)]; np != NULL; np = np->next) if (strcmp(s, np->name) == 0) return np; /* found */ return NULL; /* not found */ } char *strdup(char *); /* install: put (name, defn) in hashtab */ struct nlist *install(char *name, char *defn) { struct nlist *np; unsigned hashval; if ((np = lookup(name)) == NULL) { /* not found */ np = (struct nlist *) malloc(sizeof(*np)); if (np == NULL || (np->name = strdup(name)) == NULL) return NULL; hashval = hash(name); np->next = hashtab[hashval]; hashtab[hashval] = np; } else /* already there */ free((void *) np->defn); /*free previous defn */ if ((np->defn = strdup(defn)) == NULL) return NULL; return np; } char *strdup(char *s) /* make a duplicate of s */ { char *p; p = (char *) malloc(strlen(s)+1); /* +1 for ’\0’ */ if (p != NULL) strcpy(p, s); return p; }
Zwróć uwagę, że jeśli zderzają się skróty dwóch ciągów, może to prowadzić do
O(n)
czasu wyszukiwania. Możesz zmniejszyć prawdopodobieństwo kolizji, zwiększając wartośćHASHSIZE
. Pełne omówienie struktury danych można znaleźć w książce.źródło
hashval = *s + 31 * hashval;
dokładnie 31 i nic więcej?Najszybszym sposobem byłoby użyć już istniejących implementacji, jak uthash .
A jeśli naprawdę chcesz to samemu zakodować, algorytmy z programu
uthash
mogą zostać sprawdzone i ponownie użyte. Jest na licencji BSD, więc oprócz wymogu przekazania informacji o prawach autorskich, masz prawie nieograniczone możliwości z nim związane.źródło
Ze względu na łatwość implementacji trudno pokonać naiwne przeszukiwanie tablicy. Oprócz sprawdzania błędów jest to pełna implementacja (nieprzetestowana).
typedef struct dict_entry_s { const char *key; int value; } dict_entry_s; typedef struct dict_s { int len; int cap; dict_entry_s *entry; } dict_s, *dict_t; int dict_find_index(dict_t dict, const char *key) { for (int i = 0; i < dict->len; i++) { if (!strcmp(dict->entry[i], key)) { return i; } } return -1; } int dict_find(dict_t dict, const char *key, int def) { int idx = dict_find_index(dict, key); return idx == -1 ? def : dict->entry[idx].value; } void dict_add(dict_t dict, const char *key, int value) { int idx = dict_find_index(dict, key); if (idx != -1) { dict->entry[idx].value = value; return; } if (dict->len == dict->cap) { dict->cap *= 2; dict->entry = realloc(dict->entry, dict->cap * sizeof(dict_entry_s)); } dict->entry[dict->len].key = strdup(key); dict->entry[dict->len].value = value; dict->len++; } dict_t dict_new(void) { dict_s proto = {0, 10, malloc(10 * sizeof(dict_entry_s))}; dict_t d = malloc(sizeof(dict_s)); *d = proto; return d; } void dict_free(dict_t dict) { for (int i = 0; i < dict->len; i++) { free(dict->entry[i].key); } free(dict->entry); free(dict); }
źródło
Utwórz prostą funkcję skrótu i kilka połączonych list struktur, w zależności od skrótu, przypisz, do której listy połączonej chcesz wstawić wartość. Użyj skrótu do jego odzyskania.
Jakiś czas temu wykonałem prostą implementację:
źródło
GLib i gnulib
Są to prawdopodobnie najlepsze zakłady, jeśli nie masz bardziej szczegółowych wymagań, ponieważ są one powszechnie dostępne, przenośne i prawdopodobnie wydajne.
GLib: https://developer.gnome.org/glib/ przez projekt GNOME. Kilka kontenerów udokumentowanych pod adresem : https://developer.gnome.org/glib/stable/glib-data-types.html, w tym „Hash Tables” i „Balanced Binary Trees”. Licencja: LGPL
gnulib: https://www.gnu.org/software/gnulib/ przez projekt GNU. Masz na celu skopiowanie i wklejenie źródła do swojego kodu. Kilka kontenerów udokumentowanych pod adresem : https://www.gnu.org/software/gnulib/MODULES.html#ansic_ext_container, w tym „rbtree-list”, „linkedhash-list” i „rbtreehash-list”. Licencja GPL.
Zobacz też: Czy istnieją biblioteki C typu open source ze wspólnymi strukturami danych?
źródło
tutaj jest szybkie narzędzie, użyłem go do uzyskania „Matrixa” (wyciągu) z łańcucha. możesz mieć większą tablicę i zmieniać jej wartości w biegu również:
typedef struct { int** lines; int isDefined; }mat; mat matA, matB, matC, matD, matE, matF; /* an auxilary struct to be used in a dictionary */ typedef struct { char* str; mat *matrix; }stringToMat; /* creating a 'dictionary' for a mat name to its mat. lower case only! */ stringToMat matCases [] = { { "mat_a", &matA }, { "mat_b", &matB }, { "mat_c", &matC }, { "mat_d", &matD }, { "mat_e", &matE }, { "mat_f", &matF }, }; mat* getMat(char * str) { stringToMat* pCase; mat * selected = NULL; if (str != NULL) { /* runing on the dictionary to get the mat selected */ for(pCase = matCases; pCase != matCases + sizeof(matCases) / sizeof(matCases[0]); pCase++ ) { if(!strcmp( pCase->str, str)) selected = (pCase->matrix); } if (selected == NULL) printf("%s is not a valid matrix name\n", str); } else printf("expected matrix name, got NULL\n"); return selected; }
źródło
Dziwię się, że nikt nie wspomniał o zestawie bibliotek hsearch / hcreate, który chociaż nie jest dostępny w systemie Windows, ale jest wymagany przez POSIX, a zatem jest dostępny w systemach Linux / GNU.
Link zawiera prosty i kompletny podstawowy przykład, który bardzo dobrze wyjaśnia jego użycie.
Ma nawet wariant bezpieczny dla gwintów, jest łatwy w użyciu i bardzo wydajny.
źródło
Hashtable to tradycyjna implementacja prostego „Słownika”. Jeśli nie zależy Ci na szybkości lub rozmiarze, po prostu wygoogluj to . Istnieje wiele bezpłatnych wdrożeń.
oto pierwszy, który widziałem - na pierwszy rzut oka wygląda dobrze. (jest to dość proste. Jeśli naprawdę chcesz, aby zawierała nieograniczoną ilość danych, musisz dodać trochę logiki, aby „ponownie przydzielić” pamięć tabeli w miarę jej wzrostu).
powodzenia!
źródło
Kluczem jest haszowanie. Myślę, że użyj do tego tabeli odnośników i klucza haszującego. W Internecie można znaleźć wiele funkcji mieszania.
źródło
Najszybszą metodą byłoby użycie drzewa binarnego. Jego najgorszym przypadkiem jest też tylko O (logn).
źródło