为什么删除通常比在许多数据结构中插入要难得多?


33

您能想到任何特定的原因,为什么对于许多(大多数)数据结构而言,删除通常比插入要难得多吗?

快速示例:链接列表。插入是微不足道的,但是删除有一些特殊情况,使插入变得非常困难。自平衡二进制搜索树(例如AVL和Red-black)是痛苦的删除实现的经典示例。

我想说这与大多数人的想法有关:对于我们而言,以结构性方式定义事物更容易,这很容易导致插入。


4
怎么样popextract-min
coredump

5
“实施困难”更多地是心理学问题(认知以及人脑的优缺点)而不是编程问题(数据结构和算法的属性)。
outis 2015年

1
正如我认为提到的coredump一样,堆栈至少应该像添加一样容易删除(对于数组支持的堆栈,弹出只是指针递减[1],而如果您达到数组)。另外,在某些用例中,假设插入将很频繁,而删除则较少,但是这将是一个非常神奇的数据结构,其中删除的数量超过了插入。[1]为避免内存泄漏,您可能还应该使现在不可见的对弹出对象的引用为空,这是我记得的,因为Liskov的教科书没有
Foon 2015年

43
“侍者,请给这个三明治加些蛋黄酱吗?” “当然,没问题,先生。” “你还能把所有的芥末都去掉吗?” “呃……”
cobaltduck

3
为什么减法比加法更复杂?除法(或素因式分解)比乘法更复杂?根比幂运算更复杂?
亩太短了

Answers:


69

这不只是一种心境。有一些物理(即数字)原因导致删除操作更加困难。

删除时,您会在以前的位置留下一个漏洞。所得熵的技术术语是“碎片”。在链接列表中,这要求您“修补”已删除的节点并重新分配它正在使用的内存。在二叉树中,它导致树的不平衡。在内存系统中,如果新分配的块大于删除所留下的块,则会导致内存暂时闲置。

简而言之,插入比较容易,因为您可以选择要插入的位置。删除起来比较困难,因为您无法提前预测将要删除的项目。


3
对于内存中的结构或图中的结构,碎片与指针和间接关系无关紧要。在内存中,由于间接作用,各个节点在何处都无关紧要。对于列表,删除内部节点(在图表中您将在其中留有孔)的操作要比插入操作少(1个指针分配,1个空闲与1个分配和2个指针分配)。对于树,插入节点可以使树失去平衡,就像删除树一样。导致brito遇到困难的是边缘情况,而碎片无关紧要。
outis 2015年

12
我不同意插入和删除在可预测性方面有所不同。如果要插入相同的节点,则“反向修补”列表节点正是相反的情况。任何方向上的任何点都没有不确定性,并且在没有元素内在结构的任何容器中(例如,平衡的二叉树,元素偏移量之间具有严格关系的数组)根本没有“孔”。因此,恐怕我不知道您在说什么。
悄悄地

2
非常有趣,但我会说没有论点。您可以毫无问题地围绕简单/快速删除来组织数据结构。它不那么常见,也很有用。
luk32

@sqykly我认为列表是错误的选择示例,因为中间插入和中间关系同样困难。一种情况在另一种情况下分配内存。一个打开一个孔,另一个密封一个孔。因此,并非所有情况下删除都比添加复杂。
ydobonebi

36

为什么它往往比插入难于删除?数据结构在设计时更着眼于插入,而不是删除。

考虑这一点-为了从数据结构中删除某些内容,它必须首先存在。因此,您需要先添加它,这意味着最多可以有和插入一样多的删除。如果优化用于插入的数据结构,则可以确保获得的收益至少与针对删除进行了优化一样。

此外,按顺序删除每个元素有什么用?为什么不立即调用某个将其全部清除的函数(可能只是通过创建一个新函数)呢?同样,当数据结构实际包含某些内容时,它们也是最有用的。因此,实际上,删除与插入一样多的情况并不常见。

当您优化某件事时,您想优化其最常做且花费最多时间的事情。在正常使用中,数据结构元素的删除发生的频率要小于插入操作。


4
我可以想象有一个用例。为初始插入然后再供个人使用准备的数据结构。当然,这种情况很少,而且算法上也不是很有趣,因为正如您所说,这样的操作不能渐近地主导插入。实际上,也许有人希望批量插入可以带来相当不错的摊销成本,并且删除起来既快速又简单,因此它将具有复杂而实用的批量插入以及简单而快速的单个删除。当然,这是非常罕见的实际需要。
luk32

1
嗯,我认为一个例子可能是逆序向量。您可以k非常快速地添加一批元素:反向排序输入并与现有向量-合并O(k log k + n)。然后,您将获得一个结构,其中的插入操作相当复杂,但是消耗顶级u元素却是微不足道且快速的。只需最后u移动向量的结尾即可。但是,如果有人需要这样的东西,我该死的。我希望这至少可以加强您的论点。
luk32

您是否不应该针对平均使用模式进行优化,而不是最擅长的?
希夫

一个简单的FIFO工作队列通常会在大多数情况下尝试为空。一个设计良好的队列将得到很好的优化(即O(1))两个插入和删除(和非常好的一个也将支持快速并行操作,但是这是一个不同的问题)。
凯文

6

这并不难。

使用双向链表,在插入时,您将分配内存,然后将与头节点或前一个节点以及尾节点或下一个节点进行链接。删除时,您将取消完全相同的链接,然后释放内存。所有这些操作都是对称的。

假定在两种情况下都具有要插入/删除的节点。(在插入的情况下,您也要在之前插入节点,因此,从某种意义上说,插入可能会稍微复杂一些。)如果您要删除而没有删除的节点,而是要删除的负载节点,那么您当然必须首先在列表中搜索有效载荷,但这并不是删除的缺点,不是吗?

对于平衡树,这同样适用:一棵树通常需要在插入后立即删除,也需要在删除后立即保持平衡。尝试仅拥有一个平衡例程,并在每次操作之后应用它是一个好主意,而不管它是插入还是删除。如果您尝试实现始终使树保持平衡的插入操作,以及试图使树始终保持平衡的删除操作,而又没有使两者共享相同的平衡例程,那么不必要地会使您的生活变得复杂。

简而言之,没有理由为什么一个人应该比另一个人更难,而且如果您发现确实如此,那么实际上您很有可能是(非常人性化的)趋势的受害者建设性的而不是消减的,这意味着您可能以比所需复杂的方式实现删除。但这是人类的问题。从数学的角度来看,没有问题。


1
我不同意。AVL删除算法比插入更复杂。对于某些节点删除,您可能必须重新平衡整个树,这通常是递归完成的,但也可以非递归完成。插入时不必这样做。我不知道在所有情况下都可以避免整个树重新平衡的算法改进。
丹尼斯

@丹尼斯:很可能是AVL树遵循例外而不是规则。
outis 2015年

@outis IIRC,所有平衡的搜索树都具有比插入更复杂的删除例程。
拉斐尔2015年

怎么样封闭哈希哈希表?插入(相对)简单,删除起来至少很难概念化,因为您必须修复所有“本应位于索引X的事物当前位于索引Y,我们必须找到并放回去”问题。
凯文(Kevin)

3

在运行时方面,在Wikipedia上查看数据结构操作时间复杂度的比较,请注意插入和删除操作具有相同的复杂度。那里描述的删除操作是按索引删除的,您可以在其中引用要删除的结构元素。插入是按项目进行的。实际上,删除的运行时间较长是因为您通常要删除一个项目而不是其索引,因此还需要执行查找操作。该表中的大多数数据结构都不需要为插入内容额外查找,因为放置位置不依赖于项,或者该位置是在插入过程中隐式确定的。

至于认知的复杂性,这个问题有一个答案:边缘案例。删除中可能包含的内容比插入要多(一般情况下尚未确定)。然而,在某些设计中可以避免这些边缘情况中的至少一些(例如,在链表中具有前哨节点)。


2
“大多数数据结构不需要查找即可插入。” - 如?实际上,我会提出相反的主张。(您“找到”插入位置,这与稍后再次查找相同的元素一样昂贵。)
Raphael

@Raphael:应在操作复杂性链接表的上下文中阅读此答案,该表不包括find操作作为删除的一部分。为了回答您的问题,我按通用名称对结构进行了分类。在数组,列表,树,哈希表,堆栈,队列,堆和集合中,树和集合需要查找才能插入;其他的则使用与项目无关的索引(对于基本堆栈,队列和堆,仅公开1个索引,并且不支持查找)或从项目进行计算。图的使用方式可能会不同,具体取决于使用方式。
2015年

...尝试可以被视为树木;但是,如果归类为自己的结构,则在插入过程中是否存在“发现”更多是争论的问题,因此我不将其包括在内。注意数据结构列表没有考虑接口与实现的关系。同样,您的计数方式很大程度上取决于您的分类方式。我看看是否能想到一个更客观的陈述。
outis 2015年

我承认我想到了字典/设置界面(与CS相同)。无论如何,该表是令人误解的,并且在某些地方(iirc)甚至是错误的-Wikipedia,CS错误信息的源头。:/
拉斐尔

0

最重要的是涉及数据引用完整性。对于SQL中最合适的数据结构(如SQL中的数据库),Oracle参照完整性非常重要。
为确保您不会意外破坏它而发明了许多不同的东西。
例如,在删除时级联,它不仅删除您尝试删除的内容,还会触发相关数据的清除。
这样可以清除垃圾数据中的数据库,并保持数据完整性。
例如,在第二个表中,您有具有父项和种类的表作为相关记录。
父母是主表。如果没有适当的参照完整性,则可以删除任何表中的任何记录,以后再也不知道如何获取完整的家庭信息,因为子表中有数据,而父表中没有任何数据。
这就是为什么引用完整性检查将不允许您清除父表中的记录,直到清除子表中的记录。
这就是为什么在大多数数据源中删除数据更加困难。


我认为问题是在询问诸如链接列表,哈希表等内存结构,而不是数据库,但是即使对于内存结构,引用完整性也是一个主要问题。
2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.