C K&;R用这个链表在这里做什么?
我正在用C语言的字典进行黑客攻击,我从K&R的书中撕下了一些代码。我不明白他们是如何用这个在哈希表中建立一桶链表的??在我看来,他们正在将下一个指针链接到链接列表的标题。他们是否以某种方式创造了一个我无法捕捉的水桶?np是一个三成员结构,包含字符串(name,defn)和指向下一个字符串的指针,如果字典中存在np,则查找查找C K&;R用这个链表在这里做什么?,c,data-structures,linked-list,C,Data Structures,Linked List,我正在用C语言的字典进行黑客攻击,我从K&R的书中撕下了一些代码。我不明白他们是如何用这个在哈希表中建立一桶链表的??在我看来,他们正在将下一个指针链接到链接列表的标题。他们是否以某种方式创造了一个我无法捕捉的水桶?np是一个三成员结构,包含字符串(name,defn)和指向下一个字符串的指针,如果字典中存在np,则查找查找 if ((np = lookup(name)) == NULL){ // file not found np = (struct nlist *) malloc(s
if ((np = lookup(name)) == NULL){ // file not found
np = (struct nlist *) malloc(sizeof(*np));
if (NULL == np || (np->name = strdup(name)) == NULL){
return NULL;
}
hashval = hash(name);
np->next = hashtab[hashval]; // WHAT THE HECK ARE THEY DOING HERE?!?!
hashtab[hashval] = np;
}else{ // already there
free((void *)np->defn);
}
if((np->defn = strdup(defn)) == NULL){
return NULL;
}
return np;
为了让代码正常工作,我对代码进行了如下修改,但我有一种烦人的感觉,a错过了他们试图表达的一点
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);
phE->next = NULL; //if first entry set next to NULL, MOD HERE
tmpNode = hashtab[hashval];
if (tmpNode == NULL){ // EMPTY SPOT IN HASHTABLE
hashtab[hashval] = np;
}else{ //HASH COLLISION, ADD NODE TO LIST END
while (tmpNode->next != NULL){
tmpNode = tmpNode->next;
}
tmpNode->next = np;
}
}else{
free((void *) np->defn);
}
if ((np->defn = strdup(defn)) == NULL){
return NULL;
}
return np;
让我们跟踪一下这段代码,看看它的功能:
np->next = hashtab[hashval]; // WHAT THE HECK ARE THEY DOING HERE?!?!
hashtab[hashval] = np;
最初,我们的哈希表如下所示:
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
|
| +------+ +-----+
+----> | head | -> | ... |
+------+ +-----+
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
|
+-----+ | +------+ +-----+
np -> | |------+----> | head | -> | ... |
+-----+ +------+ +-----+
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
+---------+
|
v
+-----+ +------+ +-----+
np -> | |-----------> | head | -> | ... |
+-----+ +------+ +-----+
以下是np
:
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
|
+-----+ | +------+ +-----+
np -> | | +----> | head | -> | ... |
+-----+ +------+ +-----+
现在,我们设置np->next=hashtab[hashval]
。现在情况是这样的:
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
|
| +------+ +-----+
+----> | head | -> | ... |
+------+ +-----+
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
|
+-----+ | +------+ +-----+
np -> | |------+----> | head | -> | ... |
+-----+ +------+ +-----+
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
+---------+
|
v
+-----+ +------+ +-----+
np -> | |-----------> | head | -> | ... |
+-----+ +------+ +-----+
现在,新创建的单元格的下一个指针和hashtab[hashval]
都指向同一个对象。从np
的角度来看,它现在指向通过预先添加新单元格,然后使用所有现有单元格而形成的列表
最后,我们做hashtab[hashval]=np
,如下所示:
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
|
| +------+ +-----+
+----> | head | -> | ... |
+------+ +-----+
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
|
+-----+ | +------+ +-----+
np -> | |------+----> | head | -> | ... |
+-----+ +------+ +-----+
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
+---------+
|
v
+-----+ +------+ +-----+
np -> | |-----------> | head | -> | ... |
+-----+ +------+ +-----+
这会将新元素拼接到链接列表的前面
换言之,这是一个非常典型的列表前置器,使用链表指针数组使其变得更为复杂。让我们通过跟踪此代码来了解它的功能:
np->next = hashtab[hashval]; // WHAT THE HECK ARE THEY DOING HERE?!?!
hashtab[hashval] = np;
最初,我们的哈希表如下所示:
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
|
| +------+ +-----+
+----> | head | -> | ... |
+------+ +-----+
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
|
+-----+ | +------+ +-----+
np -> | |------+----> | head | -> | ... |
+-----+ +------+ +-----+
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
+---------+
|
v
+-----+ +------+ +-----+
np -> | |-----------> | head | -> | ... |
+-----+ +------+ +-----+
以下是np
:
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
|
+-----+ | +------+ +-----+
np -> | | +----> | head | -> | ... |
+-----+ +------+ +-----+
现在,我们设置np->next=hashtab[hashval]
。现在情况是这样的:
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
|
| +------+ +-----+
+----> | head | -> | ... |
+------+ +-----+
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
|
+-----+ | +------+ +-----+
np -> | |------+----> | head | -> | ... |
+-----+ +------+ +-----+
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
+---------+
|
v
+-----+ +------+ +-----+
np -> | |-----------> | head | -> | ... |
+-----+ +------+ +-----+
现在,新创建的单元格的下一个指针和hashtab[hashval]
都指向同一个对象。从np
的角度来看,它现在指向通过预先添加新单元格,然后使用所有现有单元格而形成的列表
最后,我们做hashtab[hashval]=np
,如下所示:
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
|
| +------+ +-----+
+----> | head | -> | ... |
+------+ +-----+
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
|
+-----+ | +------+ +-----+
np -> | |------+----> | head | -> | ... |
+-----+ +------+ +-----+
+---+---+---+--- ---+---------+---+---+---+
| | | | ... | hashval | | | |
+---+---+---+--- ---+---------+---+---+---+
|
+---------+
|
v
+-----+ +------+ +-----+
np -> | |-----------> | head | -> | ... |
+-----+ +------+ +-----+
这会将新元素拼接到链接列表的前面
换句话说,这是一个非常典型的列表前缀,使用链表指针数组会使它变得有点棘手。您看到的是将新节点作为列表中的第一个节点插入的基本习惯用法 如果
head
指向列表的当前开头(空列表为空指针),并且node
指向新节点,则只需
node->next = head;
head = node;
你就完了
这两行代码正是您在引用的K&R代码中看到的
您的代码版本坚持在列表末尾插入新节点。在散列集的基本实现中,bucket中元素的顺序实际上并不重要,这就是为什么K&R实现只是在每个bucket的开头插入新节点。正如您所看到的,它非常简单和高效
如果您想按照每个bucket的节点到达的顺序存储它们,那么必须在列表的末尾添加新节点,这在您的实现中效率明显较低。但是,如果您坚持这样做,您可以使用另一种惯用的方法,这允许您避免对空桶执行特殊的if
分支
struct nlist **pnext = &hashtab[hashval];
for (; *pnext != NULL; pnext = &(*pnext)->next);
*pnext = np;
np->next = NULL;
当然,更有效的方法是为每个bucket存储两个指针:指向列表的第一个和最后一个元素。您看到的是将新节点作为列表中的第一个节点插入的基本习惯用法 如果
head
指向列表的当前开头(空列表为空指针),并且node
指向新节点,则只需
node->next = head;
head = node;
你就完了
这两行代码正是您在引用的K&R代码中看到的
您的代码版本坚持在列表末尾插入新节点。在散列集的基本实现中,bucket中元素的顺序实际上并不重要,这就是为什么K&R实现只是在每个bucket的开头插入新节点。正如您所看到的,它非常简单和高效
如果您想按照每个bucket的节点到达的顺序存储它们,那么必须在列表的末尾添加新节点,这在您的实现中效率明显较低。但是,如果您坚持这样做,您可以使用另一种惯用的方法,这允许您避免对空桶执行特殊的if
分支
struct nlist **pnext = &hashtab[hashval];
for (; *pnext != NULL; pnext = &(*pnext)->next);
*pnext = np;
np->next = NULL;
当然,一种更有效的方法是为每个bucket存储两个指针:指向列表的第一个和最后一个元素。它们插入到链接列表的头部。插入到链接列表的头部意味着插入是O(1),而不是O(n),就像遍历列表以找到结尾一样。有时,需要结束插入的列表也会在控制块中存储一个“结束”指针以避免这种情况。@0xsmash0th:What't
phE
?在代码的其他任何地方都没有提到它。它们是插入到链表的开头。插入到链表的开头意味着插入是O(1),而不是O(n),就像遍历列表以找到结尾一样。有时,需要结束插入的列表也会在控制块中存储一个“结束”指针以避免这种情况。@0xsmash0th:What'tphE
?在你的代码中没有提到它。我确实错过了他们所做的事情的要点!我从来没有想过预挂!伟大的绘画也很有帮助!我真的错过了他们所做的事情的要点!我从来没有想过预挂!伟大的绘画也很有帮助!非常有帮助,我完全有一个心理障碍,就是只需要在末尾添加一个节点。如果我将来需要挂起一个节点,我将使用这个for循环!非常有帮助,我完全有一个心理障碍,就是只需要在末尾添加一个节点。如果我将来需要挂起一个节点,我将使用这个for循环!