它看起來像我,你幾乎有它,但只需要一點幫助的細節。所以我們可以說你有一些節點結構和以下功能操作就可以了:
(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
或給它一個合適的名稱)。但希望能讓你開始。
注意事項:我沒有試圖測試此答案中給出的代碼。歡迎更正!
你有沒有考慮過拉鍊? –
@lgrapenthin,你的意思是使用拉鍊來存儲信息? – Landey
我真的不知道如何在共享數據時重新創建樹。 @lgrapenthin – Landey