是否已知索引链表的实现?

时间:2009-11-11 04:11:00

标签: language-agnostic list indexing linked-list

我的直觉告诉我没有好办法实现这一目标,但是,与斯蒂芬科尔伯特先生不同,我宁愿相信一个开发者社区而不是我的直觉......

有没有一种已知的方法可以有效地实现“两个世界中最好的”列表,一个通过索引 O(1)插入/删除(如链接列表)提供随机访问的列表?

我预见到两种可能的结果:要么“不,这是不可能的,出于以下明显的原因......”或“呃,是的,已经完成了;请看这里,这里和这里。”

8 个答案:

答案 0 :(得分:14)

我认为插入和查找都不可能获得O(1)。添加数组的时刻(甚至是花哨的可拆分向量),插入变为O(n)

根据列表的预期行为,有一些方法可以减轻损害。如果将有比插入/删除更多的查找,那么使用向量(可变大小的数组)可能更好 - 这些是合理有效的,不像数组,但比遍历列表更好(因为这些往往是列表对于数组而言,它仍然在技术上遍历列表,但列表中的每个元素通常都有其大小,这使得它更有效。)

如果插入和删除更频繁,您可以使索引构建为惰性索引,以便仅在需要时才进行。例如,插入和删除只会更改链接列表部分(并将索引标记为脏) - 只有当有人尝试使用索引时,才会重建并标记为干净。

您甚至可以通过保留第一个脏条目的记录来优化重建。这意味着如果您只在列表的后半部分插入或删除,则当有人想要使用它时,您无需重建整个索引。

我曾经实施过的解决方案是2D List。通过这个,我的意思是:

        +-----------+    +-----------+    +-----------+
List -> | count = 7 | -> | Count = 4 | -> | Count = 6 | -> null
        +-----------+    +-----------+    +-----------+
              |                |                |
              V                V                V
        +-----------+    +-----------+    +-----------+
        |    [0]    |    |    [7]    |    |   [11]    |
        +-----------+    +-----------+    +-----------+
              |                |                |
              V                V                V
        +-----------+    +-----------+    +-----------+
        |    [1]    |    |    [8]    |    |   [12]    |
        +-----------+    +-----------+    +-----------+
              |                |                |
              :                :                :
              :                :                :
              |                |                |
              V                V                V
        +-----------+    +-----------+    +-----------+
        |    [6]    |    |   [10]    |    |   [16]    |
        +-----------+    +-----------+    +-----------+
              |                |                |
              V                V                V
             null             null             null

虽然这样做了插入和查找O(n),但余额是正确的。在纯数组解决方案中,查找为O(1),插入为O(n)。对于纯链接列表,插入为O(1)(一旦找到插入点,当然,操作本身为O(n)),查找为O(n)

对于两者而言,2D列表为O(n)但具有较低的因子。如果您要插入,只需检查每列的第一行即可找到正确的列。然后你遍历列本身寻找正确的行。然后插入该项目并增加该列的计数。类似地,对于删除,尽管在这种情况下计数减少,并且当计数达到零时整个列被删除。

对于索引查找,您遍历列以查找正确的列,然后遍历列中的项以获取正确的项。

而且,它甚至可以通过尝试保持最大高度和宽度大致相同来自动调整。

答案 1 :(得分:4)

如果你认为O(log N) == O(1)
退房:

答案 2 :(得分:2)

当我在课程中实现链接列表时,我考虑通过存储3个附加字段来优化访问时间:列表中间的节点,最近访问的节点的索引和最近访问的节点本身。 / p>

要通过索引检索节点,我将首先查看到达给定索引处节点的所有可用路径,然后选择最便宜的方式来执行此操作。方法很简单:

  1. 从头开始到所需的指数
  2. 从最近访问的节点转到所需的索引(转发)
  3. 从最近访问的节点转到所需的索引(向后)
  4. 从中间节点转到所需索引(转发)
  5. 从中间节点转到所需索引(向后)
  6. 从节点的末尾到期望的索引
  7. 我们所需指数和起始指数差异最小的路径将是最便宜的选择。如果尚未访问任何节点,则可以将最近访问的节点设置为中间节点。当然,由于偶数个元素没有实际的中间,所以我只选择n / 2的底线。

    无论如何,我从来没有真正实现这种优化,甚至真的分析它,但我希望我能提供帮助。

答案 3 :(得分:1)

你的直觉是正确的。

链接列表是O(1)插入/删除,因为您执行的插入或删除操作的操作只是切换几个指针(您插入的对象上的一个或两个,以及一个或两个上的一个或两个)其他对象)。显然,这不会因列表的大小而改变。

跳过列表将为您提供O(logn)查找,但由于您正在维护索引,因此也意味着O(logn)插入/删除,因为该索引需要保持最新。

您需要维护用于查找的任何并行数据结构,因此您的复杂性将根据该索引的复杂性进行扩展。

你想解决一个特殊的问题吗?

例如,如果可以保证完美的哈希,则可以进行O(n)插入,删除和查找。但是,您需要提前了解有关数据的一些信息。

答案 4 :(得分:1)

哈希表怎么样?您可以通过密钥和O(1)插入/删除来获得O(1)随机访问。问题是条目是无序的。

要有效实施有序序列,请查看finger trees。它们允许O(1)访问headlast以及O(log n)对内部节点的随机访问。在O(1)中的任意一端插入或删除。值得注意的是,手指树的反转需要不变的时间。

答案 5 :(得分:0)

我不知道插入时的确切BigO(因为这会因样本大小和增长而有所不同),但Java java.util.LinkedList会立即浮现在脑海中。

http://java.sun.com/j2se/1.5.0/docs/api/java/util/LinkedList.html

编辑:是的,显然在它下面仍然是一个真正的链表,因此索引得到的可能是O(n / 2),这当然是正式的O(n)。

你总是可以浪费一大堆空间并实现一个List实现,它可以保持并行链表和数组延迟插入/删除。

答案 6 :(得分:0)

Java LinkedList对索引获取具有O(n)访问权限。 LinkedList扩展AbstractSequentialList以表明它不提供O(1)索引获取。

我建议你看看Apache's TreeList。它提供O(log n)插入/删除和O(1)索引查找。

答案 7 :(得分:0)

虽然我不认为您可以获得整数索引,但如果您使用“引用”类型,则可以使用支持哈希表。

相关问题