【问题标题】:Can you implement Binary Search Tree in Haskell with O(log n) insertion?你能用 O(log n) 插入在 Haskell 中实现二叉搜索树吗?
【发布时间】:2022-01-18 10:41:09
【问题描述】:

如果我理解正确,在 Haskell 中修改(插入或删除)二叉搜索树需要复制整个树,所以实际上它是 O(n)。有没有办法在 O(log n) 中实现它,或者编译器可能会将 O(n) 插入优化到 O(log n) “在引擎盖下”?

【问题讨论】:

  • 是什么让您认为您需要复制整棵树?对于树一般来说,你只需要将路径复制到你插入/删除的节点,即O(log n)。您的 BST 是否有其他限制使这成为不可能?
  • ,你可以插入O(log n)。由于所有数据都是不可变的,因此您可以重用原始树中的节点,从而只更新大小为 O(log n) 的路径。
  • @JörgWMittag 不,我没有具体的树案例 - 一般只是关于 BST 的问题。我想我需要考虑将插入/删除作为一条路径。如果是这样,问题必须重述为 insertion is both time O(log n) and space O(log n)? 而在命令式语言中这将是 时间 O(log n) 和速度 O(1)?.
  • 仅当您希望树保持持久,即您希望保留树的旧版本和新版本。如果你不想这样,那么你可以扔掉旧节点,只需要 O(1) 空间。如果您确实想要保留两个版本,那么保留旧版本所需的额外空间为 O(log n) – 请注意,这是您无法做到的命令式语言中的可变树,无需完全复制它。

标签: haskell binary-search-tree complexity-theory


【解决方案1】:

如果我理解正确,在 Haskell 中修改(插入或删除)二叉搜索树需要复制整个树,因此实际上使其成为 O(n)

您不需要复制整个树。事实上,让我们使用一个简单的不平衡二叉搜索树,例如:

data Tree a = Node (Tree a) a (Tree a) | Empty deriving (Eq, Show)

然后我们可以插入一个值:

insertIn :: Ord a => a -> Tree a -> Tree a
insertIn x = go
  where go Empty = Node Empty x Empty
        go n@(Node l v r)
          | x < v = Node (go l) v r
          | x > v = Node l v (go r)
          | otherwise = n

在这里,我们重用 r,以防我们构造Node (go l) v r,我们重用 l,以防我们构造Node l v (go r)。对于我们访问的每个节点,我们创建一个新节点,其中两个子树之一在新节点中使用。这意味着新树将指向与原始树相同的子树对象。

在这个例子中,新节点的数量随着 O(d) 而缩放,而 d 是树的深度。如果树是相当平衡的,那么它将插入 O(log n)

当然,您可以通过在节点中存储更多关于平衡的信息来改进算法并定义 AVL 树或红黑树,在这种情况下,您可以保证 O(log n) 插入时间。

这里所有数据都是不可变的,这有助于重用部分树:我们知道lr不能改变,所以两棵树会共享大量节点,从而减少如果要同时使用原始树和新树,则需要内存。

如果没有必要引用旧树,垃圾收集器最终会收集已被新树替换的“旧”节点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-16
    • 1970-01-01
    • 2013-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多