教科书是错的。列表的第一个成员没有可用的“previous”指针,因此需要额外的代码来查找并取消链接该元素,如果它恰好是链中的第一个(通常30%的元素是其链的头部,当将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;
table[31] = one, one->next = two , two-next = three, three->next = NULL;
one->prev = NULL, two->prev = one, three->prev = two;
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;
table[31] = one, one-next = two , two-next = three, three->next = NULL;
one->ppp = &table[31], two->ppp = &one->next, three->ppp = &two-next;
*(one->ppp) = one->next;
if (one->next) one->next->ppp = one->ppp;
注意:没有特殊情况,也不需要知道父表。 (考虑存在多个哈希表但具有相同的节点类型的情况:删除操作仍然需要知道应从哪个表中删除节点)。
通常,在 {prev,next} 场景中,通过在双向链表开头添加一个虚拟节点来避免特殊情况;但这也需要分配和初始化。