【问题标题】:delete node from binary search tree, haskell从二叉搜索树中删除节点,haskell
【发布时间】:2012-03-26 10:28:01
【问题描述】:

我正在制作一个 Haskell 函数来从二叉搜索树中删除一个节点。 我知道关于需要采取的行动的规则,具体取决于孩子的数量 目标父母有。

没有孩子 - 删除, 1 个孩子 - 替换为孩子, 2 个孩子 - 在右子树中找到最小值并将节点替换为该值, - 然后,递归删除右子树中的最小值

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 也不能正常工作。它只是在对角线上向右打印字符串。

【问题讨论】:

  • 到目前为止你有什么代码?你的特殊问题在哪里?很多 BST 问题闻起来很像家庭作业,所以我不敢回答。 (并不是说我们根本不回答作业问题,而是应该以不同的方式回答它们以帮助学习。)
  • 我真的不知道从哪里开始。
  • 从最简单的情况开始:deleteNode x Empty = ... 什么会代替 ...,即从空树中删除任何内容会产生什么结果?
  • 如果你现在所在的节点是空的,你不想删除前一个节点吗?
  • 不。如果您到达空节点,则意味着您从未找到要删除的节点。想象一下你有树 [1, 0, 2] 你说“删除 3 棵树”。你会到达 2 右侧的“Empty”,但这是否意味着你应该删除 2?

标签: haskell binary-tree


【解决方案1】:

在这些情况下,最好不要考虑从树中删除节点;最好想想如何将你拥有的树变成一棵没有你想要的节点的树。

我们来做一些案例分析:

如果树为空,则结果为空,与键无关:

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

【讨论】:

  • 感谢邮递员。你们在这里真的很有帮助,我很感激
  • @pat 如果我观察这棵树并想从中删除 6 会发生什么,例如: 2 / \ 1 6 / \ 4 7 \ /\ 5 3 8 它会从右下角找到最小值树并将其分配给目标节点位置,而在下面提到的情况下它将不起作用如果我想删除 6 怎么办?它将从右子树返回 3 作为 min 并且如果将其放置在目标节点位置 6 那么在该级别 4 会发生什么? bcz 那么 BST 将较小的元素放在左边,较大的元素放在右边的属性是否有效?
  • 假设 BST 属性在初始树中成立,右子树中的最小节点必须大于左子树中的所有节点并且小于右子树中的所有其他节点。因此,一棵以该节点为根,以原始左子树为左子树的树,以及移除该节点作为其右子树的原始右子树,仍然具有 BST 属性。
【解决方案2】:

你不能!一切都是不可变的!

你可以做一棵树,它与旧树完全相同,除了删除了一个节点。 (别担心,您的编译器不需要复制太多内存。请记住,一切都是不可变的。这意味着实现可以安全地重用公共部分!)

因此,您的 deleteNode 函数将不是String -&gt; BST 类型,而是String -&gt; BST -&gt; 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。他们会极大地帮助你。

一旦你有了一个二叉树的拉链,你就可以使用拉链函数来删除树中的节点。如果您需要帮助为您的二叉搜索树数据结构实现 zipper,请告诉我,我会扩展它。现在可能有点矫枉过正了。

请注意,拉链不会为您重新平衡二叉搜索树。如果你想从你的二叉搜索树中删除一个节点保持平衡,那就是一个全新的蠕虫罐。

您可以使用number 中的common 平衡算法,具体取决于您的喜好。我建议先让它以一种不平衡的方式工作,然后如果你在平衡它时遇到问题,请提出单独的问题。

当然,如果您想要一个高效的、开箱即用的、已经实现的平衡二叉搜索树在 haskell 中 - 只需导入 Data.Map

【讨论】:

  • 谢谢,我只是使用自己制作的函数将树简化为列表,删除项目,然后使用另一个函数将其放回树中。不过,我将列表转换为树的功能无法正常工作。你对此有什么想法吗?
  • 视情况而定。它有什么问题?通过转换到列表/从列表转换,您失去了二叉树的很多好处,如果您使用树结构来发挥自己的优势,您的算法将更快(O(log n)而不是O(n))。毕竟,这就是树结构的全部意义所在。
  • 是的,明白这一点,但这是我能想到的最快的事情,哈哈。它应该查看列表,将第一个元素放在树的顶部,第二个元素根据它与第一个元素的比较来分支,依此类推。我认为我的算法已关闭
  • 可能。要么发布你在 cmets 中的内容,要么发布另一个关于它的问题,我会尽力提供帮助。也就是说,您最简单的路线可能是使用上面答案中的模板实现 deleteNode :-)
  • 您的树到列表、列表删除、列表到树的方法很可能会导致树严重不平衡(除非您非常小心)。
【解决方案3】:

这是在 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)

【讨论】:

  • 有必要把Key带到delete'功能吗?因为已经找到了钥匙。
  • 是否定义了treeMaximum
猜你喜欢
  • 2019-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多