【问题标题】:Time complexity of node deletion in singly- and doubly-linked lists单链表和双链表中节点删除的时间复杂度
【发布时间】:2010-12-26 03:07:10
【问题描述】:

为什么在双向链表中删除节点的时间复杂度(O(1))比在单链表中删除节点的时间复杂度(O(n))快?

【问题讨论】:

    标签: linked-list complexity-theory time-complexity singly-linked-list doubly-linked-list


    【解决方案1】:

    问题假设要删除的节点是已知的,并且指向该节点的指针可用。

    为了删除一个节点并将前一个节点和下一个节点连接在一起,您需要知道它们的指针。在双向链表中,两个指针在要删除的节点中都可用。在这种情况下,时间复杂度是恒定的,即 O(1)。

    而在单链表中,指向前一个节点的指针是未知的,只能通过从头遍历列表直到到达具有指向要删除的节点的下一个节点指针的节点才能找到。这种情况下的时间复杂度是 O(n)。

    如果要删除的节点仅通过值知道,则必须搜索列表,并且在单链表和双链表中时间复杂度都变为 O(n)。

    【讨论】:

    • 这对于从需要 O(n) 复杂度的单链表中删除节点是不正确的 - 请参阅下面的答案。有一个技巧,您可以从被删除的节点中复制下一个节点的值,然后跳过该节点以指向该节点,从而消除遍历列表的任何需要。
    【解决方案2】:

    实际上单链表中的删除也可以在O(1)中实现。

    给定一个具有以下状态的单链表:

    SinglyLinkedList:
       Node 1 -> Node 2
       Node 2 -> Node 3
       Node 3 -> Node 4
       Node 4 -> None
    
       Head = Node 1
    

    我们可以这样实现delete Node 2

    Node 2 Value <- Node 3 Value
    Node 2 -> Node 4
    

    这里我们将Node 2的值替换为其下一个节点的值(Node 3),并将其下一个值指针设置为Node 3的下一个值指针(Node 4),有效地跳过了现在“重复”Node 3。因此不需要遍历。

    【讨论】:

    • 有道理,但在删除最后一个元素时不会真正起作用,除非您引用了前一个(倒数第二个)节点。
    • 在这种情况下,@peterfields 您只需完全删除节点,这样前一个节点的引用将不再指向任何内容。 GC 等的细节显然取决于技术和实现(例如,您可以使用弱引用)。
    • 在尝试替换某些内容之前,您应该先沿着列表查找已删除的节点。这与沿着列表直到找到已删除节点的前任基本相同。实际上,您的方式涉及向前迈出的额外一步,因此它比规范的 O(n) 方式更糟糕
    • @mangusta 这是不正确的。您所描述的是搜索操作,然后是删除操作。删除操作已经知道要删除哪个节点。如果对常见数据结构的时间复杂性有疑问,最好参考 bigocheatsheet.com。
    • 好吧,好吧,我没有假设复制数据。如果节点具有大量数据字段,则复制操作过于昂贵。附言。我肯定不是需要备忘单的人
    【解决方案3】:

    因为你不能向后看……

    【讨论】:

      【解决方案4】:

      在已知位置插入和删除是 O(1)。但是,找到该位置是 O(n),除非它是列表的头部或尾部。

      当我们谈论插入和删除复杂性时,我们通常假设我们已经知道这将发生在哪里。

      【讨论】:

        【解决方案5】:

        这与修复要删除的节点之前的节点中的下一个指针的复杂性有关。

        【讨论】:

          【解决方案6】:

          除非要删除的元素是头(或第一个)节点,否则我们需要遍历到要删除的节点之前的节点。因此,在最坏的情况下,即当我们需要删除最后一个节点时,指针必须一直到达倒数第二个节点,从而遍历 (n-1) 个位置,这给了我们 O(n) 的时间复杂度.

          【讨论】:

            【解决方案7】:

            我不认为它是 O(1),除非你知道 必须删除的节点.....不要循环到达必须从头中删除的节点????

            如果您拥有必须删除的节点地址,则为 O(1),因为您拥有它的 prev node link 和 next node link 。 由于您拥有所有必要的链接,只需通过重新排列链接然后 free() 将“感兴趣的节点”从列表中删除。

            但是在单个链表中,您必须从 head 遍历以获得它的上一个和下一个地址,无论您是否拥有要删除的节点的地址或节点位置(如 1st ,2nd ,10th等等,。)将被删除。

            【讨论】:

              【解决方案8】:

              假设有一个从 1 到 10 的链表,你必须删除给定位置的节点 5。

              1 -> 2 -> 3 -> 4 -> 5-> 6-> 7-> 8 -> 9 -> 10
              

              您必须将 4 的下一个指针连接到 6 才能删除 5。

              1. 双向链表 您可以使用5上的前一个指针转到4。然后您可以这样做
              4->next = 5->next;
              

              Node* temp = givenNode->prev;
              temp->next = givenNode->next;
              

              时间复杂度 = O(1)

              1. 单链表 由于您在单链表中没有前一个指针,因此您不能向后移动,因此您必须从头遍历列表
              Node* temp = head;
              while(temp->next != givenNode)
              {
                temp = temp->next;
              }
              temp->next = givenNode->next;
              

              时间复杂度 = O(N)

              【讨论】:

                猜你喜欢
                • 2019-10-05
                • 2011-09-03
                • 2014-05-01
                • 2012-01-10
                • 2016-03-03
                • 2016-09-15
                • 2015-04-15
                • 1970-01-01
                • 2019-07-02
                相关资源
                最近更新 更多