二叉树

111 阅读8分钟

用对称性递归解决问题

问题分类

  1. 不需要构造辅助函数。这种问题分为两类,一类是单树问题,且不需要用到子树的某一部分,只要利用根节点左右子树的对称性即可进行递归。第二种是双树问题,即本身题目要求比较两棵树
  2. 需要构造辅助函数。这类题目通常只用根节点子树对称性无法完全解决问题,必须要用到子树的某一部分进行递归,即要调用辅助函数比较两个部分子树

不需要构造辅助函数

leetcode.100

  • 链接leetcode.cn/problems/sa…
  • 解题方法:
    特判:如果两树均为空则相同
    返回值:树相同的条件(根节点都存在+根节点相同+左儿子相同+右儿子相同)
  • leetcode解题代码
class Solution {
public:
    bool isSameTree(TreeNode* p, TreeNode* q) {
        if (!p && !q) return true;
        return p && q && p->val == q->val && isSameTree(p->left, q->left) && isSameTree(p->right, q->right); 
    }
};

leetcode.104

  • 链接leetcode.cn/problems/ma…
  • 解题方法: 特判:空树的最大深度为0
    返回值:返回左儿子深度和右儿子深度的最大值+1
  • leetcode解题代码
class Solution {
public:
    int maxDepth(TreeNode* root) {
        if (!root) return 0;
        return max(maxDepth(root->left), maxDepth(root->right)) + 1;
    }
};

leetcode.965

  • 链接leetcode.cn/problems/un…
  • 解题方法: 特判:空树为单值二叉树
    如果左儿子存在且和根节点不相等或右儿子存在且和根节点不相等返回false
    返回值:返回左儿子和右儿子
  • leetcode解题代码
class Solution {
public:
    bool isUnivalTree(TreeNode* root) {
        if (!root) return true;
        if ((root->left && root->left->val != root->val) || (root->right && root->right->val != root->val)) return false;
        return isUnivalTree(root->left) && isUnivalTree(root->right);
    }
};

leetcode.559

  • 链接leetcode.cn/problems/ma…
  • 解题方法:
    特判:根节点不存在直接返回0
    逻辑:遍历到叶子节点,更新最大深度
    返回值:返回最大深度
  • leetcode解题代码
class Solution {
public:
    int maxDepth(Node* root) {
        if (!root) return 0;
        int depth = 0;
        for (auto c: root->children)
            depth = max(depth, maxDepth(c));
        return depth + 1;
    }
};

leetcode.111

  • 链接leetcode.cn/problems/mi…
  • 解题方法:
    特判:根节点不存在直接返回0,如果不存在儿子节点返回1
    逻辑:左儿子存在,每递归一次都更新答案,右儿子存在同样每递归一次更新答案
    返回值:返回最大深度
  • leetcode解题代码
class Solution {
public:
    int minDepth(TreeNode* root) {
        if (!root) return 0;
        int res = INT_MAX;
        if (!root->left && !root->right) res = 1;
        if (root->left) res = min(res, minDepth(root->left) + 1);
        if (root->right) res = min(res, minDepth(root->right) + 1);
        return res;
    }
};

leetcode.222

  • 链接leetcode.cn/problems/co…
  • 解题方法:
    特判:根节点不存在直接返回0
    返回值:返回左儿子和右儿子节点之和+1
  • leetcode解题代码
class Solution {
public:
    int countNodes(TreeNode* root) {
        if (!root) return 0;
        return countNodes(root->left) + countNodes(root->right) + 1;
    }
};

leetcode.404

  • 链接leetcode.cn/problems/su…
  • 解题方法:
    特判:根节点不存在或只有根节点不存在叶子节点直接返回0
    逻辑:根节点的左儿子如果为叶子节点记录根节点左儿子叶子节点的总和,同样记录根节点右儿子为叶子节点的总和,相加即为答案 返回值:返回答案
  • leetcode解题代码
class Solution {
public:
    int sumOfLeftLeaves(TreeNode* root) {
        if (!root) return 0;
        if (!root->left && !root->right) return 0;
        int sum_l = sumOfLeftLeaves(root->left);
        if (root->left && !root->left->left && !root->left->right)
            sum_l += root->left->val;
        int sum_r = sumOfLeftLeaves(root->right);
        return sum_l + sum_r;
    }
};

需要构造辅助函数

leetcode.101

  • 链接leetcode.cn/problems/sy…
  • 解题方法: 辅助函数:判断左右子树为根节点的子树是否对称
    主函数特判:空树对称 返回值:返回根节点的左右子树
  • leetcode解题代码
class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        if (!root) return true;
        return dfs(root->left, root->right);
    }
    bool dfs(TreeNode* p, TreeNode* q){
        if (!p && !q) return true;
        if (!p || !q) return false;
        return p && q && p->val == q->val && dfs(p->left, q->right) && dfs(p->right, q->left);
    }
};

leetcode.110

  • 链接leetcode.cn/problems/ba…
  • 解题方法:
    辅助函数:返回以左右子树为根节点的子树深度的最大值
    主函数特判:空树为平衡二叉树 返回值:平衡二叉树定义(左右子树高度差<=1 + 左子树为平衡二叉树+右子树为平衡二叉树)
  • leetcode解题代码
class Solution {
public:
    bool isBalanced(TreeNode* root) {
        if (!root) return true;
        return abs(depth(root->left) - depth(root->right)) <= 1 && isBalanced(root->left) && isBalanced(root->right);
    }

    int depth(TreeNode* root){
        if (!root) return 0;
        return max(depth(root->left), depth(root->right)) + 1;
    }
};

leetcode.572

  • 链接leetcode.cn/problems/su…
  • 解题方法:
    辅助函数:判断root的左右子树为根节点和subroot是否相同
    主函数特判:如果root和subroot都为空则返回false
    如果满足辅助函数则返回true
    返回值:subroot是否是root的子树(subroot的左子树或右子树和subroot相同)
  • leetcode解题代码
class Solution {
public:
    bool isSubtree(TreeNode* root, TreeNode* subRoot) {
        if (!subRoot) return true;
        if (!root) return false;
        return dfs(root, subRoot) || isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);
    }

    bool dfs(TreeNode* p, TreeNode* q){
        if (!p && !q) return true;
        else if (!p || !q) return false;
        else if (p && q && p->val != q->val) return false;
        return dfs(p->left, q->left) && dfs(p->right, q->right);
    }
};

剑指offer.26

  • 链接leetcode.cn/problems/sh…
  • 解题方法:
    辅助函数:判断B是否为A的左右子树为根节点的子树的子结构
    主函数特判:空树不是任意数的子结构
    如果A的根节点和B的根节点相同,判断辅助函数
    返回值:B是否是A的左子树的子结构或A的右子树的子结构
  • leetcode解题代码
class Solution {
public:
    bool isSubStructure(TreeNode* A, TreeNode* B) {
        if (!A && !B) return false;
        if (!A || !B) return false;
        if (A->val == B->val && dfs(A, B))
            return true;
        return isSubStructure(A->left, B) || isSubStructure(A->right, B);
    }

    bool dfs(TreeNode* p, TreeNode* q){
        if (!p && !q) return false;
        if (!p || !q) return false;
        if (p->val == q->val) 
            return dfs(p->left, q->left) && dfs(p->right, q->right);
        return false;
    }
};

二叉树路径问题

问题分类

  1. 自顶向下: 就是从某一个节点(不一定是根节点),从上向下寻找路径,到某一个节点(不一定是叶节点)结束
  2. 非自顶向下: 就是从任意节点到任意节点的路径,不需要自顶向下

自顶向下

leetcode.257

  • 链接leetcode.cn/problems/bi…
  • 解题方法:
    递归返回值:返回路径答案
    递归终止条件:遍历到叶子节点将当前路径加入答案并返回
    递归思路:左儿子存在递归左儿子,右儿子存在递归右儿子
  • leetcode解题代码
class Solution {
public:
    vector<string> res;
    vector<string> binaryTreePaths(TreeNode* root) {
        dfs(root, "");
        return res;
    }

    void dfs(TreeNode* root, string path){
        if (!root) return;
        path += to_string(root->val);
        if (!root->left && !root->right){
            res.push_back(path);
            return;
        }
        dfs(root->left, path+"->");
        dfs(root->right, path+"->");
    }
};

leetcode.113

  • 链接leetcode.cn/problems/pa…
  • 解题方法:
    递归返回值:返回路径答案
    递归终止条件:遍历到叶子节点且当前总和为0时将当前路径加入答案并返回
    递归思路:左儿子存在递归左儿子,右儿子存在递归右儿子
  • leetcode解题代码
class Solution {
public:
    vector<vector<int>> res;
    vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
        vector<int> path;
        dfs(root, targetSum, path);
        return res;
    }

    void dfs(TreeNode* root, int sum, vector<int> path){
        if (!root) return;
        sum -= root->val;
        path.push_back(root->val);
        if (!root->left && !root->right && sum == 0){
            res.push_back(path);
            return;
        }
        dfs(root->left, sum, path);
        dfs(root->right, sum, path);
    }
};

leetcode.437

  • 链接leetcode.cn/problems/pa…
  • 解题方法:
    递归返回值:返回路径数量
    递归终止条件:遍历到总和为0时答案+1
    递归思路:左儿子存在递归左儿子,右儿子存在递归右儿子,由于路径不需要从根节点开始,所以还需要以root的左右子树为起点进行递归
  • leetcode解题代码
class Solution {
public:
    int res = 0;
    int pathSum(TreeNode* root, int targetSum) {
        if (!root) return 0;
        dfs(root, targetSum);// 以root为起点递归
        pathSum(root->left, targetSum);// 以root的左子树为起点递归
        pathSum(root->right, targetSum);// 以root的右子树为起点递归
        return res;
    }

    void dfs(TreeNode* root, long sum){
        if (!root) return;
        sum -= root->val;
        if (sum == 0){
            res ++;// 找到符合条件的路径还要继续找,不return
        }
        dfs(root->left, sum);
        dfs(root->right, sum);
    }
};

非自顶向下

leetcode.124

  • 链接leetcode.cn/problems/bi…
  • 解题方法:
    递归返回值:返回左右子树较长的路径+根节点值
    递归终止条件:根节点不存在返回0
    递归思路:左儿子存在递归左儿子,如果左儿子为负则取0;右儿子存在递归右儿子,如果右儿子为负则取0;更新答案
  • leetcode解题代码
class Solution {
public:
    int res = INT_MIN;
    int maxPathSum(TreeNode* root) {
        maxPath(root);
        return res;
    }
    int maxPath(TreeNode* root){// 以root为路径起点的最长路径
        if (!root) return 0;
        int l = max(maxPath(root->left), 0);// 路径为负则取0
        int r = max(maxPath(root->right), 0);
        res = max(res, l + r + root->val);// 左右路径最长的值+根节点值即为答案
        return max(l + root->val, r + root->val);// 返回左右路径较长的值
    }
};

leetcode.687

  • 链接leetcode.cn/problems/lo…
  • 解题方法:
    递归返回值:返回左右子树较长的路径
    递归终止条件:根节点不存在返回0
    递归思路:左儿子存在递归左儿子,如果左儿子的值和根节点值相同则l+1;右儿子存在递归右儿子,如果右儿子的值和根节点值相同则r+1;更新答案
  • leetcode解题代码
class Solution {
public:
    int res = INT_MIN;
    int longestUnivaluePath(TreeNode* root) {
        if (!root) return 0;
        longestpath(root);
        return res;
    }

    int longestpath(TreeNode* root){
        if (!root) return 0;
        int l = longestpath(root->left);
        int r = longestpath(root->right);
        if (root->left && root->val == root->left->val) l ++;
        else l = 0;
        if (root->right && root->val == root->right->val) r ++;
        else r = 0;
        res = max(res, l + r);
        return max(l, r);
    }
};

leetcode.543

  • 链接leetcode.cn/problems/di…
  • 解题方法:
    递归返回值:返回左右子树较长的路径
    递归终止条件:根节点不存在返回0
    递归思路:递归左儿子,如果左儿子存在l+1否则l=0;递归右儿子,如果右儿子存在r+1否则r=0;更新答案
  • leetcode解题代码
class Solution {
public:
    int res = INT_MIN;
    int diameterOfBinaryTree(TreeNode* root) {
        longestpath(root);
        return res;
    }

    int longestpath(TreeNode* root){
        if (!root) return 0;
        int l = longestpath(root->left);
        int r = longestpath(root->right);
        if (root->left) l ++;
        else l = 0;
        if (root->right) r ++;
        else r = 0;
        res = max(res, l + r);
        return max(l, r);
    }
};