Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Algorithm 为什么使用双链表删除哈希表的元素是O(1)?_Algorithm_Hashtable_Doubly Linked List - Fatal编程技术网

Algorithm 为什么使用双链表删除哈希表的元素是O(1)?

Algorithm 为什么使用双链表删除哈希表的元素是O(1)?,algorithm,hashtable,doubly-linked-list,Algorithm,Hashtable,Doubly Linked List,在CLRS的教科书“算法简介”中,第258页有这样一段 如果列表是双链接的,我们可以在O(1)时间内删除一个元素。(请注意,CHAINED-HASH-DELETE以元素x而不是其键k作为输入,因此我们不必首先搜索x。如果哈希表支持删除,则其链接列表应为双链接,以便我们可以快速删除项目。如果列表仅为单链接,则要删除元素x,我们首先必须在列表中找到x因此,我们可以更新x的前一个属性。对于单链表,删除和搜索将具有相同的渐近运行时间) 让我困惑的是,这个大家长,我没有理解它的逻辑。对于双链表,仍然需要找

在CLRS的教科书“算法简介”中,第258页有这样一段

如果列表是双链接的,我们可以在O(1)时间内删除一个元素。(请注意,CHAINED-HASH-DELETE以元素x而不是其键k作为输入,因此我们不必首先搜索x。如果哈希表支持删除,则其链接列表应为双链接,以便我们可以快速删除项目。如果列表仅为单链接,则要删除元素x,我们首先必须在列表中找到x因此,我们可以更新x的前一个属性。对于单链表,删除和搜索将具有相同的渐近运行时间)


让我困惑的是,这个大家长,我没有理解它的逻辑。对于双链表,仍然需要找到x才能删除它,这与单链表有什么不同?请帮助我理解它

一般来说,您是正确的-您发布的算法将元素本身作为输入,而不仅仅是其键:

请注意,CHAINED-HASH-DELETE将元素x而不是它的 按k键,这样我们就不必先搜索x了


你有元素x——因为它是一个双链接列表,你有指向前置和后继的指针,所以你可以在O(1)中修复这些元素——只有一个链接列表才有后继元素,所以你必须在O(n)中搜索前置元素。

教科书是错误的。列表的第一个成员没有可用的“previous”指针,因此,如果元素恰好是链中的第一个元素,则需要额外的代码来查找和取消链接该元素(如果N=M,则通常30%的元素是其链的头部,(当将N个项目映射到M个插槽时;每个插槽都有一个单独的链。)

编辑:

使用反向链接更好的方法是使用指向我们的链接的指针(通常是列表中上一个节点的->下一个链接)

然后删除变为:

*(p->pppar) = p->nxt;
这种方法的一个很好的特点是,它同样适用于链上的第一个节点(其pppar指针指向某个不是节点一部分的指针)

更新2011-11-11

因为人们看不到我的观点,我将尝试举例说明。例如,有一个哈希表
(基本上是一个指针数组)
一组节点
一个
两个
三个
其中一个必须删除

    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;
            }
现在很明显,obove代码是O(1),但有一点很糟糕:它仍然需要
数组
,索引
31
,因此在大多数情况下节点是“自包含的”,并且指向某个节点的指针足以将其从其链中删除,但当该节点恰好是其链中的第一个节点时,除外;然后需要其他信息来查找
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,NEX}场景中,通过在双链表的开始添加一个哑节点来避免特殊情况;但是这也需要分配和初始化。这里提出的问题是:考虑到你在看一个哈希表的一个特定元素。删除它有多高?

假设您有一个简单的链表:

v ----> w ----> x ----> y ----> z
                |
            you're here
现在,如果您删除
x
,则需要将
w
连接到
y
以保持列表的链接。您需要访问
w
,并告诉它指向
y
(您希望有
w-->y
)。但是您不能从
x
访问
w
,因为它是简单链接的!因此您必须遍历所有列表,在O(n)操作中找到
w
,然后告诉它链接到
y
。这很糟糕

然后,假设您是双重链接的:

v <---> w <---> x <---> y <---> z
                |
            you're here
vwxyz
|
你来了

很酷,你可以从这里访问w和y,这样你就可以在O(1)操作中连接这两个(
wy
)了!

在我看来,这其中的哈希表部分主要是一个小问题。真正的问题是:“我们可以在固定时间内从链表中删除当前元素吗?如果可以,如何删除?”

答案是:这有点棘手,但实际上是的,我们可以——至少通常是这样。我们(通常)不必遍历整个链表来查找上一个元素。相反,我们可以在当前元素和下一个元素之间交换数据,然后删除下一个元素

唯一的例外是当/如果我们需要/想要删除列表中的最后一项时。在这种情况下,没有下一个元素可以交换。如果确实必须这样做,就没有真正的方法可以避免找到上一个元素。但是,通常有一些方法可以避免这种情况——一种是使用sentinel inst终止列表空指针的ead。在这种情况下,由于我们从不删除具有sentinel值的节点,因此我们永远不必删除列表中的最后一项。这就给我们留下了相对简单的代码,如下所示:

template <class key, class data>
struct node {
    key k;
    data d;
    node *next;
};

void delete_node(node *item) {
    node *temp = item->next;
    swap(item->key, temp->key);
    swap(item->data, temp->data);
    item ->next = temp->next;
    delete temp;
}
模板
结构节点{
键k;
数据d;
节点*下一步;
};
void delete_节点(节点*项){
节点*temp=项目->下一步;
交换(项目->键,临时->键);
交换(项目->数据,临时->数据);
项目->下一步=临时->下一步;
删除临时文件;
}

假设您要删除一个元素x,通过使用双链接列表,您可以轻松地将x的上一个元素连接到x的下一个元素。因此,无需遍历所有列表,它将位于O(1)中。

查找(x)
通常是O(1)表示链式哈希表,这无关紧要
template <class key, class data>
struct node {
    key k;
    data d;
    node *next;
};

void delete_node(node *item) {
    node *temp = item->next;
    swap(item->key, temp->key);
    swap(item->data, temp->data);
    item ->next = temp->next;
    delete temp;
}
unordered_map<value,node*>mp;