在用C编写程序时,我想念的一件事就是字典数据结构。用C实现一个最方便的方法是什么?我不是在寻找性能,而是希望从头开始编写它。我也不希望它是通用的-像string-> int这样的东西。但是我确实希望它能够存储任意数量的项目。
这更多地是作为练习。我知道有可用的第三方库。但是请考虑片刻,它们不存在。在这种情况下,实现满足上述要求的字典的最快方法是什么。
在用C编写程序时,我想念的一件事就是字典数据结构。用C实现一个最方便的方法是什么?我不是在寻找性能,而是希望从头开始编写它。我也不希望它是通用的-像string-> int这样的东西。但是我确实希望它能够存储任意数量的项目。
这更多地是作为练习。我知道有可用的第三方库。但是请考虑片刻,它们不存在。在这种情况下,实现满足上述要求的字典的最快方法是什么。
Answers:
C编程语言的 6.6节介绍了一个简单的字典(哈希表)数据结构。我不认为有用的字典实现会比这更简单。为了您的方便,我在此处重现代码。
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
。有关数据结构的完整讨论,请查阅本书。
hashval = *s + 31 * hashval;
正好是31,而不是其他?
为了易于实现,很难天真地搜索数组。除了一些错误检查之外,这是一个完整的实现(未经测试)。
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);
}
创建一个简单的哈希函数和一些结构的链接列表,具体取决于哈希值,指定在哪个链接列表中插入值。也可以使用哈希来检索它。
不久前,我做了一个简单的实现:
... #define K 16 //链接系数 结构指令 { 字符* name; / *密钥名称* / int val; / *值* / struct dict *下一个;/ *链接字段* / }; typedef struct dict dict; 字典* table [K]; int已初始化= 0; 无效putval(char *,int); 无效的init_dict() { 初始化= 1; 我 for(i = 0; iname =(字符*)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; 为(ptr = table [hsh]; ptr!=(dict *)0; ptr =(dict *)ptr->下一个) 如果(strcmp(ptr-> name,key_name)== 0) 返回ptr-> val; 返回-1; }
GLib和gnulib
如果您没有更具体的要求,这些可能是最好的选择,因为它们广泛可用,可移植并且可能高效。
GLib:GNOME项目的https://developer.gnome.org/glib/。在以下网址记录了几个容器:https : //developer.gnome.org/glib/stable/glib-data-types.html,包括“哈希表”和“平衡二叉树”。执照:LGPL
gnulib:GNU项目的https://www.gnu.org/software/gnulib/。您打算将源代码复制粘贴到您的代码中。在以下网址记录了几个容器:https : //www.gnu.org/software/gnulib/MODULES.html#ansic_ext_container,包括“ rbtree-list”,“ linkedhash-list”和“ rbtreehash-list”。GPL许可证。
另请参阅:是否有具有通用数据结构的开源C库?
这是一个快速的工具,我用它从字符串中获取“矩阵”(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;
}
令我惊讶的是,没有人提到hsearch / hcreate库集,尽管这些库在Windows上不可用,但受POSIX的委托,因此在Linux / GNU系统中可用。
该链接有一个简单而完整的基本示例,很好地解释了其用法。
它甚至具有线程安全的变体,易于使用且性能非常好。