我目前有一个以降序排列的对象的双向链接列表。 (列表是侵入性的-对象中的指针。)我的操作非常有限:
操作1-4将是固定时间,但是操作5是O(n),其中n =具有相同键值的节点数。这是因为,当这些节点增加时,必须使用相同的键值移动经过其兄弟节点,并放置在该范围之后。并且发现重新插入的位置将是O(n)。
我认为堆(堆排序堆,不是malloc堆)是一种解决方案,其中最坏的情况是O(log n)(其中n =节点数)。但是,根据我的回忆以及Google找到我的情况,它似乎总是以数组的形式实现,而不是二叉树。所以:
问题:是否有一个以二进制树的形式使用指针的堆实现(而不是一个数组)来维护典型数组实现的O()?
答案 0 :(得分:1)
执行此操作的一种常见方法是使用基于数组的堆,但是:
这保留了所有堆操作的复杂性,每个节点的开销约为1.5个指针和1个整数。 (额外的.5是由于可增长数组的实现方式所致。)
或者,您可以仅将节点链接到带有指针的树中。但是,要支持所需的操作,每个节点需要3个指针(父,左,右)
两种方法都可以正常工作,但是数组实现更简单,更快,并且使用的内存更少。
ETA:
不过,我应该指出,如果使用指针,则可以使用不同种类的堆。斐波那契堆将使您可以在固定的固定时间内减少节点的值。不过,它有点复杂,并且在实践中很慢:https://en.wikipedia.org/wiki/Fibonacci_heap
答案 1 :(得分:0)
不幸的是,书面问题的答案不是书面问题标题的答案。
解决方案1:摊销O(1)数据结构
找到了一个解决方案,其中包含所有必需操作的摊销O(1)实现。
它只是双链表的双链表。 “主”双向链接列表节点称为父节点,每个键值最多有一个父节点。父节点保留具有相同键值的子节点的双向链接列表。每个孩子还指向其父母。
添加一个具有最大可能值的节点:如果没有列表头或其值不是max,则将新节点添加到主链接列表的头。否则,将其添加到头节点的子列表的尾部。
删除具有最大可能值的(任何)节点:对于具有最高值的多个项目,我们删除哪个都无所谓。因此,如果主父母有孩子,请从孩子列表中删除尾孩子。否则,从主列表中删除父项。
删除一个值为0的(任意)节点:相同的操作。
具有最高当前值的(任意)节点的增量值::如果多个节点具有相同的键值,我们可以选择任意值,因此请选择父级的尾代子级。从子级列表中删除它。如果其增加值超过最大值,那么您就可以完成。否则,这是一个新的头节点。相反,如果没有子代,则将父级父项增加到位,如果超过最大值,则将其删除。
大于0的任何节点的递减值:如果该节点是子节点,则从子节点列表中删除,然后添加到父节点的后继节点的子节点列表中,或作为父节点之后的新节点。没有孩子的父母:如果主列表中的后继者仍然具有较小的密钥,那么您就完成了。否则,将其删除并添加为后继的后代。有孩子的父母:相同,但要促进头生孩子的位置。 这是O(n),其中n =给定大小的节点数,因为您必须更改所有子代的父代指针。但是,如果为减量选择的节点是给定大小的所有节点的父节点的几率是1 / n,则将其摊为O(1)。
主要缺点是我们在逻辑上每个节点都有7个不同的指针。如果是父角色,则需要上一个和下一个父元素,以及头和尾子元素。如果是子角色,则需要上一个和下一个孩子以及父对象。可以将它们合并为4和3指针的两个备用子结构,这样可以节省存储空间,但可以节省CPU时间(也许可能需要将未使用的指针清零以保持整洁)。更新它们都不会很快。
解决方案2:草率足够好
另一种方法就是马虎。该应用程序受益于找到分数更高的节点,但绝对完美的顺序并不重要。因此,我们可以接受可以执行O(1)的解决方案,尽管有时工作不完善,而不是用O(n)操作将节点可能从链的一端移动到另一端。
这可能是双链表的当前实现。它可以支持除O(1)中的减量之外的所有操作。它可以处理O(1)中唯一键值的递减。因为我们需要跳过具有前一个键值的其余节点,以找到第一个具有相同或更高键值的节点,所以仅将非唯一键值的减量设为O(n)。在最坏的情况下,我们可以将搜索限制在5个或10个链接之内。这也将提供名义上的O(1)解决方案。但是,某些有害的使用模式可能会慢慢导致整个列表变得无序。