BinaryTree_2_路径问题

112.路径总和

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。
叶子节点 是指没有子节点的节点。

示例 1:输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22 输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。

示例 2:输入:root = [1,2,3], targetSum = 5 输出:false
解释:树中存在两条根节点到叶子节点的路径:
(1 —> 2): 和为 3
(1 —> 3): 和为 4
不存在 sum = 5 的根节点到叶子节点的路径。

示例 3:输入:root = [], targetSum = 0 输出:false 解释:由于树是空的,所以不存在根节点到叶子节点的路径。

class Solution {
 private:
  bool ans = false;
 public:
  void dfs(TreeNode* root, int targetSum, int sum) {
    if (root == nullptr) {
      return;
    }
    if (root->left == nullptr && root->right == nullptr && sum + root->val == targetSum) {
      ans = true;
      return;
    }
    dfs(root->left, targetSum, sum + root->val);
    dfs(root->right, targetSum, sum + root->val);
    return;
  }
  bool hasPathSum(TreeNode* root, int targetSum) {
    dfs(root, targetSum, 0);
    return ans;
  }
};

257.二叉树的所有路径

给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。叶子节点 是指没有子节点的节点。

示例 1:输入:root = [1,2,3,null,5] 输出:[“1->2->5”,”1->3”]

示例 2:输入:root = [1] 输出:[“1”]

分析:
1.直接 dfs

class Solution {
 private:
  vector<string> ans;
 public:
  void dfs(TreeNode* root, string s) {
    if (root == nullptr) {
      return;
    }
    s += to_string(root->val);
    if (root->left == nullptr && root->right == nullptr) {
      ans.push_back(s);
    } else {
      s += "->";
      dfs(root->left, s);
      dfs(root->right, s);
    }
    return;
  }
  vector<string> binaryTreePaths(TreeNode* root) {
    dfs(root, "");
    return ans;
  }
};

129.求根节点到叶节点数字之和

给你一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。
每条从根节点到叶节点的路径都代表一个数字:例如,从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123 。
计算从根节点到叶节点生成的 所有数字之和 。
叶节点 是指没有子节点的节点。

示例 1:输入:root = [1,2,3] 输出:25
解释:
从根到叶子节点路径 1->2 代表数字 12
从根到叶子节点路径 1->3 代表数字 13
因此,数字总和 = 12 + 13 = 25

示例 2:输入:root = [4,9,0,5,1] 输出:1026
解释:
从根到叶子节点路径 4->9->5 代表数字 495
从根到叶子节点路径 4->9->1 代表数字 491
从根到叶子节点路径 4->0 代表数字 40
因此,数字总和 = 495 + 491 + 40 = 1026

分析:
1.dfs 遍历求和

class Solution {
 private:
  int ans = 0;
 public:
  void dfs(TreeNode* root, int cur) {
    if (root == nullptr) {
      return;
    }
    int data = cur * 10 + root->val;
    if (root->left == nullptr && root->right == nullptr) {
      ans += data;
    }
    dfs(root->left, data);
    dfs(root->right, data);
    return;
  }
  int sumNumbers(TreeNode* root) {
    dfs(root, 0);
    return ans;
  }
};

113.路径总和 II

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。

示例 1:输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22 输出:[[5,4,11,2],[5,8,4,5]]

示例 2:输入:root = [1,2,3], targetSum = 5 输出:[]

示例 3:输入:root = [1,2], targetSum = 0

分析:
1.回溯搜索

class Solution {
 private:
  vector<vector<int>> ans;
 public:
  void dfs(vector<int>& path, TreeNode* root, int targetSum, int accSum) {
    if (root == nullptr) {
      return;
    }
    path.emplace_back(root->val);
    int curSum = accSum + root->val;
    if (root->left == nullptr && root->right == nullptr && curSum == targetSum) {
      ans.emplace_back(path);
    }
    dfs(path, root->left, targetSum, curSum);
    dfs(path, root->right, targetSum, curSum);
    path.pop_back();
    return;
  }
  vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
    if (root == nullptr) {
      return ans;
    }
    vector<int> path;
    dfs(path, root, targetSum, 0);
    return ans;
  }
};

437.路径总和 III

给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。
路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

示例 1:输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8 输出:3 解释:和等于 8 的路径有 3 条,如图所示。
示例 2:输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22 输出:3

         10 
       /    \
     5       -3 
    /  \       \
   3    2       11 
  /  \   \ 
3    -2   1

分析:
1.这个题中提到的路径不需要从根节点开始, 而是累计以根节点为根节点包含下的所有 (经过或者不经过根节点) 的路径. 因此用分解问题的思维, 以根节点为根节点的所有路径总和, 等于左子树包含的路径总和 + 右子树包含的路径总和 + 以根节点为必经节点下的路径总和; 写成代码就是 pathSum(root->left) + pathSum(root->right) + rootViaSum(root), 其中 rootViaSum(root) 以根节点为必经节点下的路径总和
2.所以我们思考如何计算 rootViaSum(root) , 也就是以某个具体的根节点为根节点, 然后必须经过这个根节点的路径总和; 这里又要采用另一个分解思维: 当前的这个求和为 targetSum 的话, 可以有 3 种方式, 一种是一开始根节点就能满足路径总和, 另外两种是根节点和下面左子树凑出来路径总和, 以及根节点和右子树凑出来路径总和

class Solution {
 public:
  // rootViaSum 计算必须经过这个根节点的路径总和
  int rootViaSum(TreeNode* root, long long targetSum) {
    if (root == nullptr) {
      return 0;
    }
    // 计算必须经过这个根节点的路径总和 = 可能的根节点值等于 target的情况=1/否则0 + 根节点和下面左子树凑出来路径总和 + 根节点和下面右子树凑出来的路径总和
    return ((root->val == targetSum) ? 1 : 0) + rootViaSum(root->left, targetSum - root->val) + rootViaSum(root->right, targetSum - root->val);
  }
  int pathSum(TreeNode* root, int targetSum) {
    if (root == nullptr) {
      return 0;
    }
    // 以根节点为根节点的所有路径总和 = 左子树包含的路径总和 + 右子树包含的路径总和 + 以根节点为必经节点下的路径总和
    return rootViaSum(root, targetSum) + pathSum(root->left, targetSum) + pathSum(root->right, targetSum);
  }
};

666.路径总和 IV

对于一棵深度小于 5 的树,可以用一组三位十进制整数来表示。对于每个整数:
百位上的数字表示这个节点的深度 d,1 <= d <= 4。
十位上的数字表示这个节点在当前层所在的位置 P, 1 <= p <= 8。位置编号与一棵满二叉树的位置编号相同。
个位上的数字表示这个节点的权值 v,0 <= v <= 9。
给定一个包含三位整数的 升序 数组 nums ,表示一棵深度小于 5 的二叉树,请你返回 从根到所有叶子结点的路径之和 。
保证 给定的数组表示一个有效的连接二叉树。

示例 1:输入: nums = [113, 215, 221] 输出: 12
解释: 列表所表示的树如上所示。
路径和 = (3 + 5) + (3 + 1) = 12.

示例 2:输入: nums = [113, 221] 输出: 4 解释: 列表所表示的树如上所示。 路径和 = (3 + 1) = 4.

分析:
1.这道题一个朴素的思路是还原树然后再dfs, 这样比较复杂
2.能不能直接进行一种dfs, 且不还原树?
3.这里每个节点可以通过深度+位置唯一标识根节点的位置, root = 10 depth + pos, 正好是前2位, root = num / 10, 那么它的左孩子怎么标识?
4.左孩子 = 10
(depth + 1) + 2 * pos - 1, 右孩子 = 左孩子 + 1
5.可以把所有的结构放在一个map里面进行遍历

class Solution {
 private:
  int ans = 0;
  unordered_map<int, int> nodeHash;
 public:
  void dfs(int key, int curSum) {
    if (nodeHash.find(key) == nodeHash.end()) {
      return;
    }
    curSum += nodeHash[key];
    int depth = key / 10; 
    int pos = key % 10; 
    int left = 10 * (depth + 1) + 2 * pos - 1;
    int right = left + 1;
    if (nodeHash.find(left) == nodeHash.end() && nodeHash.find(right) == nodeHash.end()) {
      ans += curSum;
    }
    dfs(left, curSum);
    dfs(right, curSum);
    return;
  }
  int pathSum(vector<int>& nums) {
    for (auto& e : nums) {
      nodeHash[e / 10] = e % 10;
    }
    dfs(nums[0] / 10, 0);
    return ans;
  }
};

250.统计同值子树

给定一个二叉树,统计该二叉树数值相同的子树个数。
同值子树是指该子树的所有节点都拥有相同的数值。

示例:输入: root = [5,1,5,5,5,null,5], 输出: 4

         5
        / \
       1   5
      / \   \
    5   5    5

分析:
1.理解题意, 什么叫做同值子树, 对一个根节点来说
(i). 如果它的左右子树都为 nullptr, 那么算一个同值子树
(ii). 如果它的所有子节点的值都相同, 那么算一个同值子树
2.对于每个根节点来说, 需要判定一棵树所有节点都相同, 然后在这个过程中去计数同值子树的个数; 这里有一层逻辑的过渡是在于, 我如果能判断一棵树的所有节点都相同=>我就能统计同值子树的个数, 其实这个判断就是分析第1点
3.判定一棵树所有节点所有节点都相同, 等价于判定左孩子所有节点都相同, 且右孩子所有节点都相同, 且值等于当前根节点

class Solution {
 private:
  int ans = 0;
 public:
  // dfs判定一棵树所有节点都相同(节点为空也算相同)
  bool judgeAllSameDFS(TreeNode* root, int value) {
    if (root == nullptr) {
      return true;
    }
    bool lSonAllSame = judgeAllSameDFS(root->left, root->val);
    bool rSonAllSame = judgeAllSameDFS(root->right, root->val);
    if (lSonAllSame && rSonAllSame) {
      ++ans;
      if (value == root->val) {
        return true;
      }
    }
    return false;
  }
  int countUnivalSubtrees(TreeNode* root) {
    if (root == nullptr) {
      return 0;
    }
    judgeAllSameDFS(root, root->val);
    return ans;
  }
};

687.最长同值路径

给定一个二叉树的 root ,返回 最长的路径的长度 ,这个路径中的 每个节点具有相同值 。 这条路径可以经过也可以不经过根节点。
两个节点之间的路径长度 由它们之间的边数表示。

示例 1: 输入:root = [5,4,5,1,1,5] 输出:2

示例 2: 输入:root = [1,4,5,4,4,5] 输出:2

分析:
1.dfs算的是以root为节点当前搜索的同值最长路径的长度, 同时在递归的时候动态更新最长同值的路径

class Solution {
 private:
  int ans = 0;
 public:
  int longestUnivaluePathDFS(TreeNode* root) {
    if (root == nullptr) {
      return 0;
    }
    int lSonLUP = longestUnivaluePathDFS(root->left);
    int rSonLUP = longestUnivaluePathDFS(root->right);
    int leftLUP = 0;
    int rightLUP = 0;
    if (root->left != nullptr && root->left->val == root->val) {
      leftLUP = lSonLUP + 1;
    }
    if (root->right != nullptr && root->right->val == root->val) {
      rightLUP = rSonLUP + 1;
    }
    ans = max(ans, leftLUP + rightLUP);
    return max(leftLUP, rightLUP);
  }
  int longestUnivaluePath(TreeNode* root) {
    longestUnivaluePathDFS(root);
    return ans;
  }
};

226.翻转二叉树

给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

示例 1:输入:root = [4,2,7,1,3,6,9] 输出:[4,7,2,9,6,3,1]

示例 2:输入:root = [2,1,3] 输出:[2,3,1]

示例 3:输入:root = [] 输出:[]

分析:
1.尝试用两种思维解决这个问题
2.遍历思维, 从上往下翻转

class Solution {
 public:
  TreeNode* invertTree(TreeNode* root) {
    if (root == nullptr) {
      return nullptr;
    }
    TreeNode* intermediary = root->left;
    root->left = root->right;
    root->right = intermediary;
    invertTree(root->left);
    invertTree(root->right);
    return root;
  }
};

3.分解思维, 先拿到翻转的结果, 然后自下而上翻转

class Solution {
 public:
  TreeNode* invertTree(TreeNode* root) {
    if (root == nullptr) {
      return nullptr;
    }
    TreeNode* l = invertTree(root->left);
    TreeNode* r = invertTree(root->right);
    root->left = r;
    root->right = l;
    return root;
  }
};

转载请注明来源, from goldandrabbit.github.io

💰

×

Help us with donation