教科书错了。列表的第一个成员没有可用的“前一个”指针,因此如果它恰好是链中的第一个元素,则需要额外的代码来查找和取消链接(通常 30% 的元素是其链的头部,如果N=M,(当将 N 个项目映射到 M 个插槽时;每个插槽都有一个单独的链。))
编辑:
使用反向链接的更好方法是使用指向指向我们的链接的指针(通常是列表中前一个节点的 ->next 链接)
struct node {
struct node **pppar;
struct node *nxt;
...
}
然后删除变为:
*(p->pppar) = p->nxt;
这种方法的一个很好的特点是它对链上的第一个节点同样有效(其 pppar 指针指向某个不属于节点的指针。
更新 2011-11-11
因为人们看不到我的意思,所以我会尝试说明。例如,有一个哈希表table
(基本上是一个指针数组)和一堆节点one
, two
,three
其中一个必须被删除。
struct node *table[123];
struct node *one, *two,*three;
/* Initial situation: the chain {one,two,three}
** is located at slot#31 of the array */
table[31] = one, one->next = two , two-next = three, three->next = NULL;
one->prev = NULL, two->prev = one, three->prev = two;
/* How to delete element one :*/
if (one->prev == NULL) {
table[31] = one->next;
}
else {
one->prev->next = one->next
}
if (one->next) {
one->next->prev = one->prev;
}
现在很明显,上面的代码是 O(1),但是有一些讨厌的东西:它仍然需要array
,和索引31
,所以在大多数情况下,一个节点是“自包含”的,指向一个节点的指针就足以删除它从它的链中取出,除非它恰好是它的链中的第一个节点;然后需要额外的信息来查找table
和31
。
接下来,考虑具有指向指针的等效结构作为反向链接。
struct node {
struct node *next;
struct node **ppp;
char payload[43];
};
struct node *table[123];
struct node *one, *two,*three;
/* Initial situation: the chain {one,two,three}
** is located at slot#31 of the array */
table[31] = one, one-next = two , two-next = three, three->next = NULL;
one->ppp = &table[31], two->ppp = &one->next, three->ppp = &two-next;
/* How to delete element one */
*(one->ppp) = one->next;
if (one->next) one->next->ppp = one->ppp;
注意:没有特殊情况,也不需要知道父表。(考虑存在多个哈希表但具有相同节点类型的情况:删除操作仍然需要知道应该从哪个表中删除节点)。
通常,在 {prev,next} 场景中,通过在双链表的开头添加一个虚拟节点来避免特殊情况;但这也需要分配和初始化。