我正在阅读的很多内容都说删除双向链表(DLL)中的内部元素是O(1)
;但为什么会这样呢?
我理解为什么SLL为O(n)
;遍历列表O(n)
并删除O(1)
但是您是否仍需要遍历DLL中的列表以查找元素?
答案 0 :(得分:15)
对于双向链接列表,一旦知道元素,就会一直移除元素。
对于单个链接列表,一旦你知道它的和它的前身,它就是一个永久的时间去除它。
由于您指向的链接会将单个链接列表删除显示为O(n)
,将双重链接列表显示为O(1)
,因此您可以确定已经知道要删除该元素的位置,但不其他任何东西。
在这种情况下,对于双向链接列表,您只需使用prev
和next
指针将其删除,即可获得O(1)
。忽略你在头部或尾部的边缘情况,这意味着:
corpse->prev->next = corpse->next
corpse->next->prev = corpse->prev
free (corpse)
但是,在您只知道要删除的节点的单个链接列表中,您不能使用corpse->prev
来获取它之前的节点,因为是 no {{1链接。
您必须通过从头部遍历列表来查找找到上一个项目,查找具有您要删除的元素的prev
的列表。这将需要next
,之后再次O(n)
进行实际删除,例如(为了简单起见,忽略边缘情况):
O(1)
那是为什么这篇文章中的两个复杂性不同。
顺便说一句,单链表中有优化可以删除lefty = head
while lefty->next != corpse:
lefty = lefty-> next
lefty->next = corpse->next
free (corpse)
(一旦找到要删除的项目,删除就会有效地为O(1)。项目)。在代码术语中,这类似于:
O(n)
答案 1 :(得分:4)
如上所述,链接指向:
更改内部元素的成本基于已经有一个指向它的指针,如果您需要先找到该元素,则还需要检索该元素的成本。
因此,对于DLL和SLL,线性搜索都是O(n),通过指针删除是O(1)。
答案 2 :(得分:2)
DLL中删除的复杂性为O(1)
。
如果提供指向前一个元素的指针而不是元素本身,它也可以是SLL中的O(1)
。
这种复杂性是假设您知道元素的位置。
即。操作签名类似于remove_element(list* l, link* e)
在这两种情况下,搜索元素都是O(n)
。
答案 3 :(得分:1)
@Matuku:你是对的。
我谦卑地不同意这里的大多数答案,试图证明DLL的删除操作是O(1)。不是。
让我解释一下。
为什么我们要考虑我们'将'指向要删除的节点的指针的场景? LinkedLists(Singly / Doubly)是线性遍历的,这就是他们的定义。它们只有指向头/尾的指针。我们怎么能突然有一个指向其间某个节点的指针?这违背了这种数据结构的目的。按照这个假设,如果我有一个100万个节点的DLL列表,那么我还需要保持100万个指针(让我们称之为访问指针)指向每个节点,以便我可以在O中删除它们( 1)?那么我将如何存储这100万个访问指针?我怎么知道哪个访问指针指向我想要删除的正确数据/节点?
我们能否拥有一个真实世界的例子,我们拥有指向必须在100%时间内删除的数据的指针?
如果您知道要删除的节点的确切位置/指针/引用,为什么要使用LinkedList?只需使用数组!这就是阵列的用途 - 直接访问你想要的东西!
通过假设您可以直接访问DLL中所需的任何节点,这违反了LinkedList作为概念数据结构的整体思路。所以我同意OP,他是对的。我会坚持这一点 - 双重LinkedLists 不能有O(1)删除任何节点。你仍然需要从头部或尾部开始,这将它降低到O(n)。
“如果”我们有指向要删除的节点的指针说X,那么当然它是O(1)因为我们有指向下一个节点的指针我们可以删除X.但是如果是想象的那么大,不是真实的。
我们不能使用名为LinkedLists的神圣数据结构的定义来解决我们可能不时出现的一些奇怪的假设。