你能用 O(log n) 插入在 Haskell 中实现二叉搜索树吗?
Can you implement Binary Search Tree in Haskell with O(log n) insertion?
如果我没理解错的话,在Haskell[=20=中修改(插入或删除)一个二进制搜索树 ] 需要复制整棵树,所以实际上是 O(n)。有没有办法在 O(log n) 中实现它,或者编译器可能会优化 O(n) 插入到 O(log n)“幕后”?
If I understand correctly, modifying (insertion or deletion) a Binary Search Tree in Haskell requires copying the whole tree, so practically making it being 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 <b>r</b>
| x > v = Node <b>l</b> 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)插入时间。
这里所有数据都是不可变的,这有助于重用部分树:我们知道 l
和 r
不能改变,所以这两棵树将共享大量节点如果您想同时使用原始树和新树,则可以减少所需的内存量。
如果没有必要引用旧树,垃圾收集器最终将收集已被新树替换的“旧”节点。
如果我没理解错的话,在Haskell[=20=中修改(插入或删除)一个二进制搜索树 ] 需要复制整棵树,所以实际上是 O(n)。有没有办法在 O(log n) 中实现它,或者编译器可能会优化 O(n) 插入到 O(log n)“幕后”?
If I understand correctly, modifying (insertion or deletion) a Binary Search Tree in Haskell requires copying the whole tree, so practically making it being 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 <b>r</b>
| x > v = Node <b>l</b> 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)插入时间。
这里所有数据都是不可变的,这有助于重用部分树:我们知道 l
和 r
不能改变,所以这两棵树将共享大量节点如果您想同时使用原始树和新树,则可以减少所需的内存量。
如果没有必要引用旧树,垃圾收集器最终将收集已被新树替换的“旧”节点。