为啥在链表中间插入 O(1)?
Posted
技术标签:
【中文标题】为啥在链表中间插入 O(1)?【英文标题】:Why is inserting in the middle of a linked list O(1)?为什么在链表中间插入 O(1)? 【发布时间】:2010-10-24 20:21:53 【问题描述】:根据Wikipedia article on linked lists,在链表中间插入被认为是O(1)。我认为这将是O(n)。您不需要找到可能在列表末尾附近的节点吗?
这个分析是否没有说明节点操作的发现(尽管它是必需的)而只是插入本身?
编辑:
与数组相比,链表有几个优点。在列表的特定点插入元素是一个常数时间操作,而在数组中插入可能需要移动一半或更多元素。
以上陈述对我来说有点误导。如果我错了,请纠正我,但我认为结论应该是:
数组:
找到插入/删除点 O(1) 执行插入/删除 O(n)链接列表:
找到插入/删除点 O(n) 执行插入/删除 O(1)我认为唯一不必找到位置的情况是,如果你保留某种指向它的指针(在某些情况下,就像头部和尾部一样)。所以我们不能直截了当地说链表在插入/删除选项方面总是优于数组。
【问题讨论】:
【参考方案1】:您是对的,文章将“索引”视为单独的操作。所以插入本身是 O(1),但到达中间节点是 O(n)。
【讨论】:
在同一位置插入超过 1 个对象时会产生更大的差异... @Anony-Mousse 你能再解释一下吗?即插入多个对象时只需要找到一次插入位置? 现有列表的大小为 O(n),而不是您计划在那里进行的插入次数。【参考方案2】:插入本身是 O(1)。节点查找是 O(n)。
【讨论】:
【参考方案3】:不,当您决定要插入时,假定您已经在迭代列表中。
链接列表上的操作通常以这样一种方式完成,即它们并不真正被视为通用“列表”,而是作为节点的集合——将节点本身视为主循环的迭代器。因此,当您浏览列表时,您会注意到作为业务逻辑的一部分,需要添加一个新节点(或删除一个旧节点)并且您这样做了。您可以在一次迭代中添加 50 个节点,每个节点只需 O(1) 时间即可断开两个相邻节点的链接并插入新节点。
【讨论】:
【参考方案4】:为了与图表显示的数组进行比较,它是 O(1),因为您不必将所有项目移动到新节点之后。
所以是的,他们假设您已经拥有指向该节点的指针,或者获取指针是微不足道的。换句话说,问题是这样的:“给定 X 处的节点,在这个节点之后插入的代码是什么?”您可以从插入点开始。
【讨论】:
【参考方案5】:插入链表不同于遍历链表。您不是在定位项目,而是在重置指针以将项目放在那里。无论是在前端附近还是接近尾端插入都没有关系,插入仍然涉及重新分配指针。当然,这取决于它是如何实现的,但这就是列表的优势——您可以轻松插入。通过索引访问是数组的亮点。然而,对于一个列表,通常需要 O(n) 才能找到第 n 个项目。至少那是我在学校时记得的。
【讨论】:
【参考方案6】:因为它不涉及任何循环。
插入是这样的:
插入元素 链接到上一个 链接到下一个 完成无论如何,这都是恒定的时间。
因此,一个接一个地插入 n 个元素是 O(n)。
【讨论】:
【参考方案7】:一旦你知道你要把它放在哪里,插入是 O(1)。
【讨论】:
【参考方案8】:这个分析是否没有说明节点操作的发现(尽管它是必需的)而只是插入本身?
你明白了。在给定点插入假定您已经持有指向要在其后插入的项目的指针:
InsertItem(item * newItem, item * afterItem)
【讨论】:
【参考方案9】:不,它不考虑搜索。但是,如果您已经持有指向列表中间项的指针,则在该点插入是 O(1)。
如果一定要搜索,就得加上搜索时间,应该是O(n)。
【讨论】:
【参考方案10】:这篇文章是关于比较数组和列表的。查找数组和列表的插入位置都是O(N),本文忽略。
【讨论】:
不会找到数组的插入点是 O(1) 吗?由于数组存储在连续的内存中,它所要做的就是添加偏移量。 @vg1890 - 你必须先找到偏移量。【参考方案11】:O(1) 取决于您有一个项目,您将在其中插入新项目。 (之前或之后)。如果你不这样做,那就是 O(n),因为你必须找到那个项目。
【讨论】:
【参考方案12】:我认为这只是您选择计入 O() 表示法的一个例子。在插入正常操作的情况下进行计数是复制操作。对于数组,在中间插入涉及将位置上方的所有内容复制到内存中。使用链表,这变成了设置两个指针。无论插入什么,您都需要找到位置。
【讨论】:
【参考方案13】:如果您有在操作后插入的节点的引用对于链表来说是 O(1)。 对于数组,它仍然是 O(n),因为您必须移动所有后续节点。
【讨论】:
【参考方案14】:最常见的情况可能是在列表的开头或结尾插入(而列表的结尾可能很快就可以找到)。
相比之下,在数组的开头或结尾插入项目(如果数组位于末尾则需要调整数组大小,如果数组位于开头则需要调整大小并移动所有元素)。
【讨论】:
如果在末尾保留一个空元素的缓冲区,则可以使将项目插入数组末尾的时间为 O(1),尽管有时插入仍然是 O(1)。大多数收藏都是这样做的。也可以通过将索引运算符更改为返回元素编号 (n+x) % len 来使数组开头的惰性项为 O(1),其中 x 是您在开头插入项的次数的名单。双端队列有时是这样实现的(但有时也用双向链表实现。以上是关于为啥在链表中间插入 O(1)?的主要内容,如果未能解决你的问题,请参考以下文章