Быстрый способ реализации словаря в C

Одна из вещей, которые я пропускаю при написании программ на C, - это структура данных словаря. Какой самый удобный способ реализовать один в C? Я не ищу производительности, но простота его кодирования с нуля. Я не хочу, чтобы он был общим - что-то вроде string- > int. Но я хочу, чтобы он мог хранить произвольное количество элементов.

Это больше похоже на упражнение. Я знаю, что есть сторонние библиотеки, которые можно использовать. Но подумайте, что их не существует. В такой ситуации самый быстрый способ реализовать словарь, удовлетворяющий вышеуказанным требованиям.

Ответ 1

Раздел 6.6 Язык программирования C представляет собой простую структуру данных словаря (хеш-таблицы). Я не думаю, что полезная реализация словаря может быть проще, чем это. Для вашего удобства я воспроизвожу код здесь.

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;
}

Обратите внимание, что если хэши двух строк сталкиваются, это может привести к времени поиска O(n). Вы можете уменьшить вероятность столкновений, увеличив значение HASHSIZE. Для полного обсуждения структуры данных, пожалуйста, обратитесь к книге.

Ответ 2

Самый быстрый способ - использовать уже существующую реализацию, например uthash.

И, если вы действительно хотите его самостоятельно закодировать, алгоритмы из uthash могут быть рассмотрены и повторно использованы. Это BSD-лицензированная так, кроме требования передать уведомление об авторских правах, вы довольно безграничны в том, что вы можете с ней сделать.

Ответ 3

Для простоты реализации трудно наивно искать массив. Помимо некоторой проверки ошибок, это полная реализация (непроверенная).

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);
}

Ответ 4

Создайте простую хеш-функцию и некоторые связанные списки структур, в зависимости от хэша, назначьте связанный список, чтобы вставить значение. Используйте хеш для его извлечения.

Я сделал простую реализацию некоторое время назад:

...
#define K 16 // chaining coefficient

struct dict
{
    char *name; /* name of key */
    int val;   /*  value */
    struct dict *next; /* link field */
};

typedef struct dict dict;
dict *table[K];
int initialized = 0;


void  putval ( char *,int);

void init_dict()
{   
    initialized = 1;
    int i;  
    for(i=0;iname = (char *) malloc (strlen(key_name)+1);
    ptr->val = sval;
    strcpy (ptr->name,key_name);


    ptr->next = (struct dict *)table[hsh];
    table[hsh] = ptr;

}


int getval ( char *key_name )
{   
    int hsh = hash(key_name);   
    dict *ptr;
    for (ptr = table[hsh]; ptr != (dict *) 0;
        ptr = (dict *)ptr->next)
    if (strcmp (ptr->name,key_name) == 0)
        return ptr->val;
    return -1;
}

Ответ 5

вот быстрый инструмент, я использовал его для получения "Матрицы" (sruct) из строки. вы можете иметь больший массив и изменить его значения в прогоне:

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;
}

Ответ 6

Хэш-таблица - это традиционная реализация простого словаря. Если вам не нужна скорость или размер, просто google для него. Существует множество свободно доступных реализаций.

здесь первый, который я видел - с первого взгляда, это выглядит хорошо для меня. (это довольно просто. Если вы действительно хотите, чтобы он хранил неограниченное количество данных, вам нужно добавить некоторую логику для "перераспределения" памяти таблицы по мере ее роста.)

Удачи!

Ответ 7

GLib и gnulib

Это ваши вероятные лучшие ставки, если у вас нет более конкретных требований, поскольку они широко доступны, переносимы и, вероятно, эффективны.

Смотрите также: Существуют ли библиотеки с открытым кодом C с общими структурами данных?

Ответ 8

Хеширование - это ключ. Я думаю, что для этого используется таблица поиска и хеширующий ключ. Вы можете найти множество функций хэширования в Интернете.

Ответ 9

Самый быстрый способ - использовать двоичное дерево. Его худшим случаем является также только O (logn).

Ответ 10

Я удивлен, что никто не упоминал hsearch/hcreate набор библиотек, который, хотя он недоступен в Windows, но является стандартным с linux/GNU

Он даже имеет поточно-безопасный вариант, прост в использовании и очень эффективен.