为什么要删除单个链表O(1)?

时间:2012-12-27 00:57:51

标签: data-structures linked-list singly-linked-list

我不清楚为什么在单个链接列表的末尾删除的时间是O {1},正如wikipedia article所说的那样。

单个链表由节点组成。节点包含某种数据,以及对下一个节点的引用。链表中最后一个节点的引用为空。

--------------    --------------           --------------
| data | ref | -> | data | ref | -> ... -> | data | ref |
--------------    --------------           --------------

我确实可以删除O(1)中的最后一个节点。但是在这种情况下,您不会将新的最后一个节点(前一个节点)的引用设置为null,因为它仍包含对已删除的最后一个节点的引用。所以我想知道他们在运行时分析中是否忽略了这一点?或者它是否被认为你不必改变它,因为引用,好吧,只是指向什么,并且这被视为null?

因为如果它不会被忽略,我会认为删除是O(n)。因为您必须遍历整个列表才能到达新的最后一个节点并将其引用也设置为null。只有双链表才真正是O(1)。

CNC中 也许这种观点会让人更加清晰。但我看到“删除节点”成功删除节点本身并将之前的引用设置为null。

8 个答案:

答案 0 :(得分:32)

我不确定我在维基百科文章中看到它可以在O(1)时间内删除单链表的最后一个条目,但在大多数情况下该信息不正确。给定链表中的任何单个节点,通过在该新节点周围重新布线列表,总是可以在O(1)时间之后删除其后的节点。因此,如果给了一个指向链表中倒数第二个节点的指针,那么你可以在O(1)时间内删除列表的最后一个元素。

但是,如果你没有任何额外的指针进入列表而不是头指针,那么你不能删除列表的最后一个元素而不扫描到列表的末尾,这将需要Θ(n)正如你所指出的那样。你是绝对正确的,只是删除最后一个节点而不先将指针更改为它是一个非常糟糕的主意,因为如果你这样做,现有的列表将包含一个指向解除分配对象的指针。

更一般地说 - 在单个链接列表中进行插入或删除的成本是O(1),假设您有一个指向要插入或删除的节点之前的节点的指针。但是,您可能需要做额外的工作(最多Θ(n))才能找到该节点。

希望这有帮助!

答案 1 :(得分:7)

ANY 位置添加/删除 ANY 节点为O(1)。代码只是以固定成本(少量指针计算和malloc / frees)来添加/删除节点。对于任何特定情况,此算术成本都是固定的。

但是,达到(索引)所需节点的成本是O(n)。

本文仅列出多个子类别中的添加/删除(在中间,开头,结尾添加),以显示在中间添加的成本与在开头/结尾添加的成本不同(但相应的成本仍然是固定的)

答案 2 :(得分:0)

例如,您可以在最后一个(“从结尾开始”之后)和删除时指向元素: 1.删​​除此“第二个结束”元素的下一个*。 2.在NULL

旁边设置“从结尾开始”*

答案 3 :(得分:0)

O(1)仅仅意味着“不变成本”。这并不意味着1次操作。这意味着“最多C”操作,无论其他参数如何变化(例如列表大小),C都是固定的。事实上,在有时令人困惑的大喔世界:O(1)== O(22)。

相比之下,删除整个列表的成本为O(n),因为成本会随着列表的大小(n)而变化。

答案 4 :(得分:0)

如果你包括修复悬空节点的费用,你仍然可以在O(1)中使用末端的sentinel节点(也在该页面上描述)。

你的"空" list以单个sentinel开头

Head -> [Sentinel]

添加一些东西

Head -> 1 -> 2 -> 3 -> [Sentinel] 

现在通过将3为无效的节点标记为删除tail(3),然后删除指向旧sentinel的链接,并为其释放内存:

Head -> 1 -> 2 -> 3 -> [Sentinel] 
Head -> 1 -> 2 -> [Sentinel] -> [Sentinel] 
Head -> 1 -> 2 -> [Sentinel]

答案 5 :(得分:0)

为了将来的参考,我必须说经过一些研究后我发现,回答这个问题所提供的论据都没有关系。答案是我们只是决定堆栈的顶部是链表的头部(而不是尾部)。这将在推送例程中引入一些细微的变化,但是弹出和推送都将保持为o(1)。

答案 6 :(得分:0)

如果要在单个链表中指定要删除的节点,则可以通过简单地将下一个节点复制到要删除的节点上,在固定时间内删除该节点。

M pointer to node to delete
M.data=M.next.data
M.next=M.next.next

否则,如果您需要搜索节点,那么您不能比O(n)

做得更好

答案 7 :(得分:0)

是的,即使您不维护指向“上一个元素”的指针,也可以在O(1)时间执行此操作。

假设您有此列表,其中“ head”和“ tail”是静态指针,“ End”是标记为结束的节点。您可以照常使用“ next == NULL”,但是在这种情况下,您必须忽略该值:

head -> 1 -> 2 -> 3 -> 4 -> 5 -> End <- tail

现在,您将获得一个指向节点3的指针,但没有指向其先前节点的指针。您也有头和尾指针。这是一些Python风格的代码,假设使用SingleLinkedList类。

class SingleLinkedList:

    # ... other methods

    def del_node(self, node):  # We "trust" that we're only ever given nodes that are in this list.
        if node is self.head:
            # Simple case of deleting the start
            self.head = node.next
            # Free up 'node', which is automatic in python
        elif node.next is self.tail
            # Simple case of deleting the end. This node becomes the sentinel
            node.value = None
            node.next = None
            # Free up 'self.tail'
            self.tail = node
        else:
            # Other cases, we move the head's value here, and then act
            # as if we're deleting the head.
            node.value = self.head.value
            self.head = self.head.next
            new_head = self.head.next
            # Free up 'self.head'
            self.head = new_head

A,这会重新排序列表,会在周围移动值,这可能适合您的应用程序,也可能不合适。