# 1. 路径相关的题目
257. 二叉树的所有路径 - E (opens new window)
所有从根节点到叶子节点的路径
113. 路径总和 II - M (opens new window)
求满足条件的所有路径(从根节点到叶子节点)
112. 路径总和 - E (opens new window)
从根节点到叶子节点 路径总和等于给定目标和的路径。
988. 从叶结点开始的最小字符串 - M (opens new window)
求所有路径中的最小字符串(从叶子节点开始)
129. 求根节点到叶节点数字之和 - M (opens new window)
437. 路径总和 III - M (opens new window)
路径: 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
# 2. 解题思路
关于二叉树路径的题目,首先要明确一点:
是否是从(根节点->叶子节点 || 叶子节点->根节点)
# 2.1 根节点-->叶子节点
# 2.1.1 二叉树的所有路径(根->叶子)
257. 二叉树的所有路径 - E (opens new window)
这道题是用String来表示路径的。(为了效率可以采用StringBuilder)
public class Solution{
List<String> result = new ArrayList();
public List<String> binaryTreePaths(TreeNode root) {
dfs(root,new StringBuilder());
return result;
}
// 根->叶。 DFS完美
public void dfs(TreeNode root, StringBuilder path){
if(root == null){
return ;
}
path.append(root.val);
if(root.left == null && root.right == null){
result.add(path.toString());
}
path.append("->");
// 有些同学,可能出于优化代码的角度,想把new StringBuilde(path)也提取出来,这个不可以的,
dfs(root.left, new StringBuilder(path));
dfs(root.right,new StringBuilder(path));
}
}
113. 路径总和 II - M (opens new window)
求满足条件的所有路径(从根节点到叶子节点)
这道题和257 (opens new window) 基本一样,区别在于用List来代表路径。解法自然是一样。
public class Solution{
List<List<Integer>> result = new ArrayList();
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
dfs(root,new ArrayList(),targetSum);
return result;
}
public void dfs(TreeNode root,List<Integer> path,int targetSum){
if(root == null){
return;
}
path.add(root.val);
targetSum -= root.val;
if(root.left == null && root.right == null && targetSum == 0){
result.add(path);
}
// 这里我们发现遍历左右子树,都得重新构建路径,还能优化。
dfs(root.left, new ArrayList(path),targetSum);
dfs(root.right,new ArrayList(path),targetSum);
}
}
上面的解法遍历左右子树都得重新构建路径,这里进行优化一下:
只在添加的时候构建路径。
具体做法如下:
public class Solution{
List<List<Integer>> result = new ArrayList();
List<Integer> path = new ArrayList();
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
dfs(root,targetSum);
return result;
}
public dfs(TreeNode root, int targetSum){
if(root == null) return ;
targetSum -= root.val;
path.add(root.val);
if(root.left == null && root.right == null && targetSum == 0){
result.add(new Array(path));
}
dfs(root.left,targetSum);
dfs(root.right,targetSum);
// 这个地方很容易忽略, 向上回溯
path.remove(path.size()-1);
}
}
112. 路径总和 - E (opens new window)
从根节点到叶子节点 路径总和等于给定目标和的路径。
跟上面的题目一样,都是采用DFS来解题。
public class Solution{
boolean result = false;
public boolean hasPathSum(TreeNode root,int targetSum){
dfs(root,targetSum);
return result;
}
public dfs(TreeNode root, int targetSum){
if(root == null) return;
targetSum -= root.val;
if(root.left == null && root.right == null && targetSum == 0){
result = true;
}
dfs(root.left,targetSum);
dfs(root.right,targetSum);
}
}
优化代码:
public class Solution{
public boolean hasPathSum(TreeNode root,int targetSum){
if(root == null){
return false;
}
targetSum -= root.val;
// 叶子节点
if(root.left == null && root.right == null){
return (targetSum == 0);
}
return hasPathSum(root.left,targetSum) || hasPathSum(root.right, target);
}
}
988. 从叶结点开始的最小字符串 - M (opens new window)
求所有路径中的最小字符串(从叶子节点开始)
对于不熟练的同学,有一个小技巧:从叶子节点开始 <==> 从根节点开始,采用头插法。
这道题只要掌握小技巧,那就跟上面的题是一个套路。无非需要排个序而已
public class Solution{
List<String> paths = new ArrayList();
public String smallestFromLeaf(TreeNode root) {
dfs(root,new StringBuilder());
if(paths.isEmpty()) return "";
// 进行排序
Collections.sort(paths);
return paths.get(0);
}
public dfs(TreeNode root,StringBuilder path){
if(root == null) return;
// 头插法
path.insert(0,(char)('a'+root.val));
if(root.left == null && root.right == null){
paths.add(path.toString());
}
dfs(root.left , new StringBuilder(path));
dfs(root.right, new StringBuilder(path));
}
}
优化一下(忽略不计)
public class Solution{
List<String> paths = new ArrayList();
StringBuilder sb = new StringBuilder();
public String smallestFromLeaf(TreeNode root) {
dfs(root);
if(paths.isEmpty()) return "";
Collections.sort(paths);
return paths.get(0);
}
public dfs(TreeNode root){
if(root == null) return ;
sb.insert(0,(char)('a'+root.val));
if(root.left == null && root.right == null){
paths.add(new String(sb));
}
dfs(root.left);
dfs(root.right);
// 回溯 (注意这里StringBuilder的删除API:deleteCharAt(int index))
sb.deleteCharAt(0);
}
}
129. 求根节点到叶节点数字之和 - M (opens new window)
此题依然是:
根节点 -> 叶子节点
这里我们先用获取所有路径的方法来做:
借助于 Integer.valueOf(String s) 来转换数字。
public class Solution{
List<String> paths = new ArrayList();
StringBuilder path = new StringBuilder();
public int sumNumbers(TreeNode root) {
dfs(root);
// 获取到所有路径之后,开始求和
int sum = 0;
for(int i = 0; i < paths.size(); i++){
sum += Integer.valueOf(paths.get(i));
}
return sum;
}
public void dfs(TreeNode root){
if(root == null){
return;
}
path.append(root.val);
if(root.left == null && root.right == null){
paths.add(new String(path));
}
dfs(root.left);
dfs(root.right);
path.deleteCharAt(path.length()-1);
}
}
上面利用Integer来转换数字可能通用性不是很好,下面我们来转换成求和来做:
sum * 10 + root.val;
public class Solution{
int sum = 0;
public int sumNumbers(TreeNode root) {
dfs(root,0);
return sum;
}
public void dfs(TreeNode root,int tmpSum){
if(root == null) return ;
tmpSum = 10 * tmpSum + root.val;
// 到达叶子节点,就求和
if(root.left == null && root.right == null){
sum += tmpSum;
}
dfs(root.left,tmpSum);
dfs(root.right,tmpSum);
}
}
这里还有更好的DFS:
public class Solution{
public int sumNumbers(TreeNode root) {
return dfs(root, 0);
}
public int dfs(TreeNode root, int preSum){
if(root == null )return 0;
preSum = preSum * 10 + root.val;
if (root.left == null && root.right == null){
return preSum;
}
return dfs(root.left, preSum)+dfs(root.right, preSum);
}
递归不要太深究细节。(结束条件、核心逻辑想清楚即可)
437. 路径总和 III - M (opens new window)
微软 - 5, 字节 - 3
这个题目比较特殊:路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)
class Solution {
int paths = 0;
public int pathSum(TreeNode root, int targetSum) {
// 从根节点开始
dfs(root,targetSum);
// 从左节点开始
pathSum(root.left,targetSum);
// 从右节点开始
pathSum(root.right,targetSum);
return paths;
}
public void dfs(TreeNode root,int sum){
if(root == null) return ;
sum -= root.val;
if(sum == 0){
paths += 1;
}
dfs(root.left, sum);
dfs(root.right, sum);
}
}
124. 二叉树中的最大路径和 - H (opens new window)
==高频题==
字节 - 11 , 微软 - 5
public class Solution{
int max = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
if(root == null) {
return 0;
}
int left = Math.max(0,maxPathSum(root.left));
int right = Math.max(0,maxPathSum(root.right));
max = Math.max(max,left+right+root.val);
return Math.max(left,right) + root.val;
}
}