我在编写从树中删除节点的代码时遇到了问题。
给定BST和键值,找到树中的键并删除它。
所以这是我的想法, 首先,如果BST为零,则返回nil 如果BST只有一个根节点,那么也返回nil。
然后,如果BST中的密钥与给定密钥匹配,请检查该节点具有的叶数。如果节点根本没有子节点,则从第一个前任(根)重新创建一个bst到该节点的最后一个前导,并共享不是前一个节点的所有其余数据。
如果节点有一个孩子,则视为没有孩子的孩子,但只是将孩子添加到最后一个孩子。
对于节点有两个孩子,我必须找到一些没有任何子节点的节点来替换它们的位置。
编写代码时出现了困难的部分,我现在不知道如何重新创建和共享树的数据。
那么有人可以提供一些提示或线索吗?
答案 0 :(得分:4)
在我看来,你几乎拥有它,但只是需要一些细节方面的帮助。因此,假设您有一些节点结构和以下函数来操作它:
(left-subtree [node])
- 返回node
的左子树,如果nil
没有左子树则返回node
(right-subtree [node])
- 返回node
的正确子树,如果nil
没有正确的子树,则返回node
。(value [node])
- 返回与node
(leaf? [node])
- 如果true
是一片叶子,则返回node
,否则false
。现在让我们编写一个without-root
函数,该函数接受(子)树并返回一个新树,其中包含原始树中除根节点外的所有内容:
(defn without-root [node]
(cond (leaf? node) nil ; resulting tree is the empty tree, return nil
(and (left-subtree node) ; two children, "difficult" case
(right-subtree node)) (handle-difficult-case node)
;; cases for single child
(left-subtree node) (left-subtree node)
(right-subtree node) (right-subtree node)))
正如您在问题中所述,“困难”的情况是node
有两个孩子。所以我决定把它拆分成一个单独的函数来促进讨论。
让我们谈谈handle-difficult-case
。由于有两个孩子,我们不知何故需要将它们组合成一棵树。如果您阅读了维基百科关于BST Deletion的内容,您基本上想要采用有序前导或后继(即左子树的最右边节点,或右子树的最左边节点)并制作它是新根。选择哪一个都没关系 - 任何一个都可以。为了便于讨论,我们将选择左子树的最右边节点。
现在我们可以编写一个新函数without-rightmost-node
,它接受一个树并返回一个没有最右边节点的新树。但是,我们还需要存储在该节点中的值。所以我们要么需要独立调用一些find-rightmost-node
函数来获取它的值(效率很低),要么返回值和新树(这会混淆函数的用途)。
相反,让我们编写一个接受树的函数,并返回一个等同于原始树的新树,除了它的根是原始树的最右边节点。为了好玩,我们可以调用这个函数percolate-rightmost-node
,因为正如我们所看到的,最右边的节点会递归地“冒泡”到(子)树的顶部。
(defn percolate-rightmost-node [node]
(if-let [right-side (right-subtree node)]
;; then (recurse down the right side)
(let [percolated (percolate-rightmost-node right-side)]
;; Construct a new tree from the result.
(with-left-subtree percolated
(with-right-subtree node (left-subtree percolated))))
;; else (we are at the rightmost node -- return it!)
node))
我觉得if-let
表达的“然后”方面不是很清楚,所以让我详细说明一下。基本上,我们采用percolated
子树,将其保留为子树(这是percolated
的唯一子项),并将其替换为node
的右子树。然后,我们将结果替换为percolated
的左子树(有效地重新生根树),生成最终结果。
percolate-rightmost-node
的输出只有一个左子树 - 它永远不会有一个正确的子树。所以在结果“冒泡”之后,我们只需要给它一个正确的子树。因此,我们可以将handle-difficult-case
实现为:
(defn handle-difficult-case [node]
(let [percolated (-> node ; Find the rightmost node of the left
(left-subtree) ; subtree and "percolate" it to the top
(percolate-rightmost-node))]
;; Now take the percolated tree. Its right subtree is empty,
;; so substitute in the right subtree of node.
(with-right-subtree percolated
(right-subtree node))))
那应该是它。当然,您需要根据代码进行调整(至少,内联handle-difficult-case
或给它一个合适的名称)。但希望这能让你开始。
警告:我没有尝试测试此答案中给出的代码。欢迎更正!
答案 1 :(得分:2)
这将是一个很长的答案,所以请让我提前道歉,指出你的书,而不是直接回答。我强烈建议您查看作为PDF格式的Purely Functional Data Structures(法律上)。虽然这本书在print/ebook中也是一本好书。
并且超短的回答是使用Clojure内置的sorted-map
如果你想在实践中这样做(虽然写你自己的意志当然得到书呆子街道信用)因为有序地图使用二进制红黑树引擎盖下