我正在使用Haskell函数从二进制搜索树中删除节点。 我知道根据孩子的数量需要采取的行动规则 目标家长有。
没有孩子 - 删除, 1个孩子 - 替换为孩子, 2个孩子 - 找到右子树中的min并用值替换节点, - 然后,递归删除右子树的最小值
data BST = MakeNode BST String BST
| Empty
deleteNode :: String -> BST
treeBuilder :: [String] -> BST
treeBuilder = foldr add Empty
add :: String -> BST -> BST
add new Empty = (MakeNode Empty new Empty)
add string tree@(MakeNode left value right)
| string > value = MakeNode left value (add string right)
| string < value = MakeNode (add string left) value right
| otherwise = tree
无法弄清楚为什么treeBuilder也无法正常工作。它只是将字符串对角打印到右侧。
答案 0 :(得分:7)
在这些情况下,最好不要考虑从树中删除节点;最好考虑如何在没有你想要的节点的情况下将你拥有的树转换成一个树。
让我们做一些案例分析:
如果树为空,则结果为空,无论键是什么:
delete _ Empty = Empty
如果树非空,我们必须查看密钥是否与节点匹配。如果它不匹配,那么我们需要根据密钥是否大于或小于节点来转换左子树或右子树:
delete key (MakeNode l key' r) | key < key' = MakeNode (delete key l) key' r
delete key (MakeNode l key' r) | key > key' = MakeNode l key' (delete key r)
如果它匹配(由于所有不匹配的情况都已处理,它必须匹配),那么我们必须弄清楚如何创建没有根节点的新树。根据您的描述,如果节点没有子节点,只需删除它:
delete _ (MakeNode Empty _ Empty) = Empty
如果节点有一个孩子,请使用:
delete _ (MakeNode l _ Empty) = l
delete _ (MakeNode Empty _ r) = r
否则,找到并删除右子树中的最小键,并将其用作新根键:
delete _ (MakeNode l _ r) = MakeNode l key r' -- make a new root with min key and new r
where key = minKey r -- find the minimum key in the right subtree
r' = delete key r -- new right subtree with min key removed
-- a helper function to find the minimum key in a tree
-- !! does not work on Empty tree !!
minKey (MakeNode Empty key _) = key
minKey (MakeNode l _ _) = minKey l
答案 1 :(得分:1)
你不能!一切都是不变的!
您可以做的是使新树与完全相同,除非删除了一个节点。 (别担心,你的编译器不需要复制很多内存。记住,所有都是不可变的。这意味着实现可以安全地重用公共部分!)
因此,您的deleteNode函数不是String -> BST
类型,它的类型为String -> BST -> BST
。 String
是您要删除的标签,第一个BST
是输入树,第二个BST
是输出树。
正如@Ingo所提到的,你可以通过实现函数来递归地实现删除:
deleteNode :: String -> BST -> BST
deleteNode _ Empty = ... -- Handle the empty case
deleteNode x (BST left a right) | x == a = ... -- Delete the node
| x < a = ... -- Recurse on the lesser node
| otherwise = ... -- Recurse on the greater node
如果你想在可遍历的数据结构(树,列表等)中做一些除删除(插入,更改等)之外的一般性修改,我建议你阅读zippers。他们会非常帮助你。
获得二叉树的拉链后,可以使用拉链函数删除树中的节点。如果您想帮助为二进制搜索树数据结构实现拉链,请告诉我,我将扩展它。现在它可能有点过头了。
请注意,拉链不会为您重新平衡二叉搜索树。如果你想从你的二进制搜索树中删除一个节点和保持平衡,那就是一堆全新的蠕虫。
根据您的喜好,您可以使用number common个{{3}}平衡算法。我建议先让它以不平衡的方式工作,如果你在平衡它时遇到问题,可以提出不同的问题。
当然,如果你想在haskell中使用一个高效,开箱即用,已经实现的平衡二进制搜索树 - 只需导入Data.Map
!
答案 2 :(得分:0)
这是使用Mutual Recursion
在Haskell中实现的删除函数树的类型是:
type Key = Int
data BST = Nil | Node Key BST BST deriving (Show, Eq)
这是删除功能:
delete :: Key -> BST -> BST
delete k Nil = Nil
delete k x@(Node a l r)
| (k < a) = Node a (delete k l) r
| (k > a) = Node a l (delete k r)
| (k == a) = delete' k x
delete' :: Key -> BST -> BST
delete' k (Node a l r)
| (l == Nil) = r
| (r == Nil) = l
| otherwise = let (k,t) = maxAndDelete l
in Node k t r
-- This function finds the maximum and then deletes the node as well
maxAndDelete :: BST -> (Key,BST)
maxAndDelete t = let m = treeMaximum t
in (m,delete m t)