[LeetCode] 865. Smallest Subtree with all the Deepest Nodes 包含最深結點的最小子樹



Given a binary tree rooted at `root`, the *depth* of each node is the shortest distance to the root.

A node is deepest if it has the largest depth possible among any node in the entire tree.

The subtree of a node is that node, plus the set of all descendants of that node.

Return the node with the largest depth such that it contains all the deepest nodes in its subtree.

Example 1:

Input: [3,5,1,6,2,0,8,null,null,7,4]
Output: [2,7,4]
Explanation:

We return the node with value 2, colored in yellow in the diagram.
The nodes colored in blue are the deepest nodes of the tree.
The input "[3, 5, 1, 6, 2, 0, 8, null, null, 7, 4]" is a serialization of the given tree.
The output "[2, 7, 4]" is a serialization of the subtree rooted at the node with value 2.
Both the input and output have TreeNode type.

Note:

  • The number of nodes in the tree will be between 1 and 500.
  • The values of each node are unique.

這道題給了我們一棵二叉樹,讓我們找包含所有最深結點的最小子樹,就是返回這棵最小子樹的根結點。題目中給了一個例子,因為有圖,所以可以很直接的看出來最深的結點是7和4,那么包含這兩個結點的最小子樹的根結點是2,返回即可。其實最深的結點不一定只有兩個,可能有很多個,比如對於一棵完全二叉樹,即把例子圖中的結點7和4去掉后,此時最深的結點就有四個,分別是6,2,0,8,都包含這些結點的子樹就是原樹本身了,要返回根結點。

通過上述分析,可以發現,子樹的最大深度很重要,對於一棵完全二叉樹來說,根結點的左右子樹的最大深度一定是相同的,此時直接返回根結點即可。若左右子樹的最大深度不同,則最深結點一定位於深度大的子樹中,可以對其調用遞歸函數。所以只需要寫一個計算最大深度的遞歸函數,來計算左右子樹的最大深度差,再根據這個差值來決定對誰調用當前的遞歸函數,兩個遞歸函數相互纏繞,畫面美極了,參見代碼如下:


解法一:
class Solution {
public:
    TreeNode* subtreeWithAllDeepest(TreeNode* root) {
        int diff = depth(root->left) - depth(root->right);
        return (diff == 0) ? root : subtreeWithAllDeepest(diff > 0 ? root->left : root->right);
    }
    int depth(TreeNode* node) {
        return !node ? 0 : max(depth(node->left), depth(node->right)) + 1;
    }
};

上面的解法其實並不高效,因為對於每個結點,都要統計其左右子樹的最大深度,有大量的重復計算存在,我們來嘗試提高時間復雜度,就不可避免的要犧牲一些空間。遞歸函數需要返回一個 pair,由每個結點的最大深度,以及包含最深結點的最小子樹組成。所以在原函數中,對根結點調用遞歸函數,並取返回的 pair 中的第二項。

在遞歸函數中,首先判斷結點是否存在,為空的話直接返回一個 {0, NULL} 對兒。否則分別對左右子結點調用遞歸函數,將各自的返回的 pair 存入 left 和 right 中,然后先分別在 left 和 right 中取出左右子樹的最大深度 d1 和 d2,之后就要建立返回值的 pair,第一項為當前結點的最大深度,由左右子樹中的最大深度加1組成,而包含最深結點的最小子樹由 d1 和 d2 值的大小決定,若 d1>d2,則為 left.second,否則為 right.second,這樣我們就把原本的兩個遞歸,揉合到了一個遞歸函數中,大大提高了運行效率,參見代碼如下:


解法二:
class Solution {
public:
    TreeNode* subtreeWithAllDeepest(TreeNode* root) {
        return helper(root).second;
    }
    pair<int, TreeNode*> helper(TreeNode* node) {
        if (!node) return {0, NULL};
        auto left = helper(node->left), right = helper(node->right);
        int d1 = left.first, d2 = right.first;
        return {max(d1, d2) + 1, (d1 == d2) ? node : (d1 > d2 ? left.second : right.second)};
    }
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/865


參考資料:

https://leetcode.com/problems/smallest-subtree-with-all-the-deepest-nodes/

https://leetcode.com/problems/smallest-subtree-with-all-the-deepest-nodes/discuss/146808/One-pass

https://leetcode.com/problems/smallest-subtree-with-all-the-deepest-nodes/discuss/146786/Simple-recursive-Java-Solution

https://leetcode.com/problems/smallest-subtree-with-all-the-deepest-nodes/discuss/146842/Short-and-concise-C%2B%2B-solution-using-DFS-3~5-lines


[LeetCode All in One 題目講解匯總(持續更新中...)](https://www.cnblogs.com/grandyang/p/4606334.html)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM