樹的理解(二):遞歸

翻轉樹

https://leetcode-cn.com/problems/invert-binary-tree/submissions/
根據翻轉樹的特徵,我們可以發現翻轉樹就是將每個節點的左子樹和右子樹調換,那麼遞歸處理每個節點,定義中間變量,將每個節點的左右指針分別指向右子樹和左子樹。

public TreeNode invertTree(TreeNode root) {
        if(root == null)
            return null;
        TreeNode cur = root.left;
        root.left = root.right;
        root.right = cur;
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }

合併二叉樹

https://leetcode-cn.com/problems/merge-two-binary-trees/
根據題意我們可以發現,如果兩個節點都不爲空時,將兩個節點的值合併,否則當一個節點不爲空時,可以直接將該節點作爲合併樹的節點。
根據這一特點,我們不難寫出以下遞歸退出條件:
當兩個節點都爲空時,返回null
當一個節點爲空時,返回另一個節點;
只有當兩個節點都不爲空時,才繼續遍歷下去。

class Solution {
    public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
        if(t1 == null && t2 == null)return null;
        if(t1 == null)return t2;
        if(t2 == null)return t1;    
        TreeNode root = new TreeNode(t1.val + t2.val);
        root.left = mergeTrees(t1.left,t2.left);
        root.right = mergeTrees(t1.right,t2.right);
        return root;
    }
}

路徑總和

https://leetcode-cn.com/problems/path-sum/
根據題意得,只有根節點遞歸至葉子節點且路徑和與sum相等時,才返回真。
那麼就分別判斷根節點的左右子樹,沒遞歸到一個節點就減去該節點的值
直到遞歸到葉子節點且葉子節點的值與剩下的值相等(路徑和等於sum)

class Solution {
    public boolean hasPathSum(TreeNode root, int sum) {
        if(root == null)return false;
        if(root.left == null && root.right == null) {
            if(sum == root.val)return true;
            else return false;
        }
        return hasPathSum(root.left,sum - root.val) || hasPathSum(root.right,sum - root.val);
    }
}

路徑總和 III

https://leetcode-cn.com/problems/path-sum-iii/

(以樹的每一個節點做入口) <-> 雙重遞歸

雙重遞歸,思路其實比較清晰,就是以每個節點爲開始,分別求以當前節點爲起始點的路徑中,是否有等於sum的路徑和,如果有,就記錄一下。
curPathSum(root,sum)的作用就是以當前節點開始求出路徑和中是否有滿足條件的個數。curPathSum(root,sum) 的路徑和一直遞歸到葉子節點,因爲樹中的值有正有負,不能因爲路徑和<0就終止遞歸。
但是由題意可以看出,這個路徑和可以不是從根節點開始的,可以從中間開始,那麼就分別求當前左右子樹的路徑和,最後將三三者彙總。

但是這種解法相當於對樹中的每個節點都做一次路徑和,效率不高。

class Solution {
    public int pathSum(TreeNode root, int sum) {
        if(root == null) return 0;
        int res = curPathSum(root,sum) + pathSum(root.left,sum) + pathSum(root.right,sum);
        return res;
    }
    private int curPathSum(TreeNode root,int sum) {
        if(root == null) return 0;
        int res = 0;
        if(root.val == sum)res++;
        res += curPathSum(root.left,sum - root.val) + curPathSum(root.right,sum - root.val);
        return res;
    }
}

另一個樹的子樹

https://leetcode-cn.com/problems/subtree-of-another-tree/

和路徑總和 III的解法思路類似。
(以樹的每一個節點做入口) <-> 雙重遞歸

s樹中的每個節點,都有可能做爲s子樹的根節點與t的根節點相同。
hasSubtree(TreeNode s, TreeNode t)具體判斷了兩棵樹是否是具有相同結構和節點值的子樹。
isSubtree(TreeNode s, TreeNode t)遞歸地將當前節點,當前節點的左子樹,當前節點的右子樹分別做了判斷。

class Solution {
    public boolean isSubtree(TreeNode s, TreeNode t) {
        if(s == null)return false;
        return hasSubtree(s,t) || isSubtree(s.left,t) || isSubtree(s.right,t);
    }
    private boolean hasSubtree(TreeNode s, TreeNode t) {
        if(s == null && t == null)return true;
        if(s == null || t == null)return false;
        if(s.val != t.val)return false;
        return hasSubtree(s.left,t.left) && hasSubtree(s.right,t.right);
    }
}

左葉子之和

https://leetcode-cn.com/problems/sum-of-left-leaves/submissions/

判斷葉子節點之和,最好在父節點判斷,如果到了葉子節點再判斷時就無法確定該節點到底是左還是右。
下面兩種方法哪個順手用拿個。

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        int[] res = {0};
        sumOfLeftLeaves(root,res);
        return res[0];
    }
    private void sumOfLeftLeaves(TreeNode node,int[] res) {
        if(node == null) return;
        if(isLeaf(node.left)) res[0] += node.left.val;
        sumOfLeftLeaves(node.left,res);
        sumOfLeftLeaves(node.right,res);
    }
    private boolean isLeaf(TreeNode node) {
        if(node == null) return false;
        return node.left == null && node.right == null;
    }
}

另一種解法:

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        if(root == null)return 0;
        if(isLeaf(root.left)) return root.left.val + sumOfLeftLeaves(root.right);
        return sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right);
    }

    private boolean isLeaf(TreeNode node) {
        if(node == null) return false;
        return node.left == null && node.right == null;
    }
}

對稱二叉樹

https://leetcode-cn.com/problems/symmetric-tree/submissions/

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root == null)return true;
        return (isSymmetric(root.left,root.right));
    }

    private boolean isSymmetric(TreeNode left,TreeNode right) {
        if(left == null && right == null)return true;
        if(left == null || right == null)return false;
        if(left.val != right.val)return false;
        return isSymmetric(left.right,right.left) && isSymmetric(left.left,right.right);
    }
}

最長同值路徑

https://leetcode-cn.com/problems/longest-univalue-path/

具體思路是根據根節點來判斷左右子節點是否和根節點相等,如果相等則表示是一個路徑,否則路徑不是一個路徑。findPath的返回值表示子節點(左子右子中選一個較長的)路徑中的最長路徑。

class Solution {
    public int longestUnivaluePath(TreeNode root) {
        int[] res = {0};
        findPath(root,res);
        return res[0];
    }
    private int findPath(TreeNode root,int[] res) {
        if(root == null) return 0;
        int left = findPath(root.left,res);
        int right = findPath(root.right,res);
        int leftPath = root.left != null && root.left.val == root.val ? left + 1 : 0;
        int rightPath = root.right != null && root.right.val == root.val ? right + 1 : 0;
        res[0] = Math.max(res[0],leftPath + rightPath);
        return Math.max(leftPath,rightPath);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章