我有以下形式的树
data Tree a = Leaf | Node (Tree a) a (Tree a)
我创建了一个函数,该函数基于从左到右的有序遍历在树中查找节点的值。
getElem :: Tree a -> Int -> Maybe a
getElem Leaf _ = Nothing
getElem (Node l x r) n
| s == n = Just x
| n < s = getElem l n
| otherwise = getElem r (n - s - 1)
where
s = size l
我现在想编写一种能够更新树的方法。它应该能够接受树,索引和值,并使用该值更新该索引处的节点。到目前为止,我有:
update :: Tree a -> Int -> a -> Tree a
update Leaf _ _ = Leaf
update (Node l x r) index c
| s == index = (Node l c r)
| index < s = update l index c
| otherwise = update r (index - s - 1) c
where
s = size l
该功能可以添加,但显然仅返回添加的节点本身。我希望能够返回带有新节点的“更新”后的整个树,或者如果索引超出范围则按原样返回树。
有人可以给我一些想法如何进行此操作吗?
编辑1:
好的,我知道我在此处重复时基本上是在丢弃树的其余部分。所以:
update :: Tree a -> Int -> a -> Tree a
update Leaf _ _ = Leaf
update (Node l x r) index c
| s == index = (Node l c r)
| index < s = update (Node l2 x r) index c
| otherwise = update (Node l x r2) (index - s - 1) c
where
s = size l
l2 = l
r2 = r
编辑2(对不起,我!)
update :: Tree a -> Int -> a -> Tree a
update Leaf _ _ = Leaf
update (Node l x r) index c
| s == index = (Node l c r)
| index < s = (Node (upd l index c) x r)
| otherwise = (Node l x (upd r (index - s - 1) c))
where
s = size l
我花了一些时间将头缠住它。谢谢你的评论!
答案 0 :(得分:4)
由于在Haskell中所有数据都是不可变的,因此您无法“更新”树,因此可以构建 new 树。但是,该树可能引用了旧树的子树。因此,您本身并不会完全构建 新树。
您设法创建了一个“更新的”节点,所以现在唯一缺少的是在 new 树中使用“更新的”子树。在那棵树中,您可以将“ old”值与 other 子树一起使用,以构建新的子树,例如:
update :: Tree a -> Int -> a -> Tree a
update Leaf _ _ = Leaf
update (Node l x r) index c
| s == index = Node l c r
| index < s = Node (update l index c) x r
| otherwise = Node l x (update r (index - s - 1) c)
where s = size l
如果您将叶子“计数”为节点,则可能还需要更改Leaf
的大小写。
带有索引的树不是很常见。为了提高性能,最好跟踪左子孩子(或两者)中的项数,因为这样我们就可以选择左子孩子或右子孩子而无需计算子孩子。通过跟踪编号,对于 complete 树,然后更新树是 O(log n)操作,而不是 O(n)操作。