在我看来,您几乎拥有它,但只需要一些细节方面的帮助。因此,假设您有一些节点结构和以下函数可以对其进行操作:
-
(left-subtree [node]) - 返回node 的左子树,如果node 没有左子树,则返回nil
-
(right-subtree [node]) - 返回node 的右子树,如果node 没有右子树,则返回nil。
-
(value [node]) - 返回与 node 关联的值
-
(leaf? [node]) - 如果node 是叶子,则返回true,否则返回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。由于有两个孩子,我们需要以某种方式将它们组合成一棵树。如果您阅读了 Wikipedia 关于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 表达式的“then”一侧不是很清楚,所以让我详细说明一下。基本上,我们采用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 或给它一个合适的名称)。但希望这能让你开始。
告诫购买者:我没有尝试测试此答案中给出的代码。欢迎指正!