二叉树路径问题

原文来自于  一篇文章解决所有二叉树路径问题(问题分析+分类模板+题目剖析)

 

 

对于刚刚接触树的问题的新手而言,路径问题是一个比较棘手的问题。题解中关于二叉树路径问题的总结还偏少,今天我用一篇文章总结一下二叉树的路径问题。学透这篇文章,二叉树路径题可以秒杀

 

问题分类

二叉树路径的问题大致可以分为两类:

1、自顶向下:

顾名思义,就是从某一个节点(不一定是根节点),从上向下寻找路径,到某一个节点(不一定是叶节点)结束

具体题目如下:

 

而继续细分的话还可以分成一般路径与给定和的路径

 

2、非自顶向下:

就是从任意节点到任意节点的路径,不需要自顶向下

 

解题模板

这类题通常用深度优先搜索(DFS)和广度优先搜索(BFS)解决,BFS较DFS繁琐,这里为了简洁只展现DFS代码

下面是我对两类题目的分析与模板

一、自顶而下:

dfs

一般路径:
vector<vector<int>>res;
void dfs(TreeNode*root,vector<int>path)
{
    if(!root) return;  //根节点为空直接返回
    path.push_back(root->val);  //作出选择
    if(!root->left && !root->right) //如果到叶节点  
    {
        res.push_back(path);
        return;
    }
    dfs(root->left,path);  //继续递归
    dfs(root->right,path);
}

# **给定和的路径:**
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);
}

 

这类题型DFS注意点:

1、如果是找路径和等于给定target的路径的,那么可以不用新增一个临时变量cursum来判断当前路径和,

  只需要用给定和target减去节点值,最终结束条件判断target==0即可

2、是否要回溯:二叉树的问题大部分是不需要回溯的,原因如下:

  二叉树的递归部分:dfs(root->left),dfs(root->right)已经把可能的路径穷尽了,

  因此到任意叶节点的路径只可能有一条,绝对不可能出现另外的路径也到这个满足条件的叶节点的;

  而对比二维数组(例如迷宫问题)的DFS,for循环向四个方向查找每次只能朝向一个方向,并没有穷尽路径,

  因此某一个满足条件的点可能是有多条路径到该点的,并且visited数组标记已经走过的路径是会受到另外路径是否访问的影响,这时候必须回溯

3、找到路径后是否要return:

  取决于题目是否要求找到叶节点满足条件的路径,如果必须到叶节点,那么就要return;

  但如果是到任意节点都可以,那么必不能return,因为这条路径下面还可能有更深的路径满足条件,还要在此基础上继续递归

4、是否要双重递归(即调用根节点的dfs函数后,继续调用根左右节点的pathsum函数):看题目要不要求从根节点开始的,还是从任意节点开始,需要双重递归

 

二、非自顶而下:

这类题目一般解题思路如下:

设计一个辅助函数maxpath,调用自身求出以一个节点为根节点的左侧最长路径left和右侧最长路径right,那么经过该节点的最长路径就是left+right
接着只需要从根节点开始dfs,不断比较更新全局变量即可

 1 int res=0;
 2 int maxPath(TreeNode *root) //以root为路径起始点的最长路径
 3 {
 4     if (!root)
 5         return 0;
 6     int left=maxPath(root->left);
 7     int right=maxPath(root->right);
 8     res = max(res, left + right + root->val); //更新全局变量  
 9     return max(left, right);   //返回左右路径较长者
10 }

 

这类题型DFS注意点:
1、left,right代表的含义要根据题目所求设置,比如最长路径、最大路径和等等

2、全局变量res的初值设置是0还是INT_MIN要看题目节点是否存在负值,如果存在就用INT_MIN,否则就是0

3、注意两点之间路径为1,因此一个点是不能构成路径的

 

例题解答

一、自顶向下

257. 二叉树的所有路径
直接套用模板1即可,注意把"->"放在递归调用中

 1 class Solution {
 2     List<String> res = new ArrayList<>();
 3     public List<String> binaryTreePaths(TreeNode root) {
 4         dfs(root,"");
 5         return res;
 6     }
 7 
 8     public void dfs(TreeNode root, String s){
 9         if(root == null){
10             return;
11         }
12         s += String.valueOf(root.val)+"->";
13         if(root.right == null && root.left == null){
14             s = s.substring(0, s.length()-2);
15             res.add(s);
16         }
17         dfs(root.left, s);
18         dfs(root.right, s);
19     }
20 }

 

面试题 04.12. 求和路径

 1 class Solution {
 2     int res = 0;
 3 
 4     public int pathSum(TreeNode root, int sum) {
 5         if (root == null){
 6             return 0;
 7         }
 8         dfs(root, sum);
 9         pathSum(root.left, sum);
10         pathSum(root.right, sum);
11         return res;
12     }
13 
14 
15     public void dfs(TreeNode root, int sum){
16         if(root == null) return;
17         sum = sum - root.val;
18         if (sum == 0){
19             res+=1;
20         }
21         dfs(root.left, sum);
22         dfs(root.right, sum);
23     }
24 }

 

112. 路径总和

 1 class Solution {
 2     boolean res = false;
 3     public boolean hasPathSum(TreeNode root, int targetSum) {
 4         dfs(root, targetSum);
 5         return res;
 6     }
 7 
 8 
 9     public void dfs(TreeNode root, int targetSum){
10         if (root == null){
11             return;
12         }
13         targetSum -= root.val;
14         if(root.left == null && root.right == null && targetSum == 0){
15             res =  res || true;
16             return;
17         }
18 
19         dfs(root.left, targetSum);
20         dfs(root.right, targetSum);
21     }
22 }

 

113. 路径总和 II

 1 class Solution {
 2 
 3     List<List<Integer>> res = new LinkedList<>();
 4 
 5     public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
 6         dfs(root, targetSum, new LinkedList<>());
 7         return res;
 8     }
 9 
10     public void dfs(TreeNode root, int targetSum, Deque<Integer> path){
11         if (root == null){
12             return;
13         }
14         path.offerLast(root.val);
15         targetSum -= root.val;
16         if(root.left == null && root.right == null && targetSum == 0){
17             res.add(new LinkedList<Integer>(path));
18         }
19 
20         dfs(root.left, targetSum, path);
21         dfs(root.right, targetSum, path);
22         path.pollLast();
23     }
24 }

 

437. 路径总和 III

 1 class Solution {
 2     int res = 0;
 3 
 4     public int pathSum(TreeNode root, int targetSum) {
 5         if(root == null){
 6             return 0;
 7         }
 8         dfs(root, targetSum);
 9         pathSum(root.left, targetSum);
10         pathSum(root.right, targetSum);
11         return res;
12     }
13 
14     public void dfs(TreeNode root, long targetSum){
15         if (root == null){
16             return;
17         }
18         targetSum -= root.val;
19         if(targetSum == 0){
20             res +=1;
21         }
22 
23         dfs(root.left, targetSum);
24         dfs(root.right, targetSum);
25     }
26 }

 

988. 从叶结点开始的最小字符串

 1 class Solution {
 2     String dict = "abcdefghijklmnopqrstuvwxyz";
 3     String s = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
 4     public String smallestFromLeaf(TreeNode root) {
 5         dfs(root, "");
 6         return s;
 7     }
 8 
 9     public void dfs(TreeNode root, String tem){
10         if(root == null){
11             return;
12         }
13         tem += dict.charAt(root.val);
14         if(root.left == null && root.right == null){
15             StringBuilder sb = new StringBuilder(tem);
16             String st = sb.reverse().toString();
17             s = s.compareTo(st)<0?s:st;
18             return;
19         }
20         dfs(root.left, tem);
21         dfs(root.right,tem);
22     }
23 }
 1 class Solution {
 2     String ans = "~";
 3     public String smallestFromLeaf(TreeNode root) {
 4         dfs(root, new StringBuilder());
 5         return ans;
 6     }
 7 
 8     public void dfs(TreeNode node, StringBuilder sb) {
 9         if (node == null) return;
10         sb.append((char)('a' + node.val));
11 
12         if (node.left == null && node.right == null) {
13             sb.reverse();
14             String S = sb.toString();
15             sb.reverse();
16 
17             if (S.compareTo(ans) < 0)
18                 ans = S;
19         }
20 
21         dfs(node.left, sb);
22         dfs(node.right, sb);
23         sb.deleteCharAt(sb.length() - 1);
24     }
25 }

 

注意:

最后一题放了两个解法,主要是要注意,java 是值传递,当dfs() 函数的参数 是一个对象的时候,地址值会被复制到函数中,在

113. 路径总和 II 一题中,如果不加上 “path.pollLast();”  最终结果 path 的长度会出现问题,为什么String 类型不会出问题呢?因为String 不可变  每次修改都是 new String(), 同理StringBuilder 就需要“先增后减”;

关于 Java 到底是值传递还是引用传递?

 

二、非自顶向下

124. 二叉树中的最大路径和

left,right分别为根节点左右子树最大路径和,注意:如果最大路径和<0,意味着该路径和对总路径和做负贡献,因此不要计入到总路径中,将它设置为0

 1 class Solution {
 2     Integer res = Integer.MIN_VALUE;
 3     public int maxPathSum(TreeNode root) {
 4         maxPath(root);
 5         return res;
 6     }
 7 
 8     int maxPath(TreeNode root){
 9         if(root == null){
10             return 0;
11         }
12         int left = Math.max(0, maxPath(root.left));
13         int right = Math.max(0, maxPath(root.right));
14         // 比较当前最大路径和与左右子树最长路径加上根节点值的较大值,更新全局变量
15         res = Math.max(res, left+right+root.val);
16         // 返回左右子树较长的路径加上根节点值
17         return Math.max(left+root.val, right+root.val);
18     }
19 }

 

687. 最长同值路径

 1 class Solution {
 2     int res = 0;
 3     public int longestUnivaluePath(TreeNode root) {
 4         dfs(root);
 5         return res;
 6     }
 7 
 8     public int dfs(TreeNode root){
 9        if(root == null){
10            return 0;
11        }
12        int left = dfs(root.left);
13        int right = dfs(root.right);
14        // 如果存在左子节点和根节点同值,更新左最长路径;否则左最长路径为0
15        if(root.left !=null && root.val == root.left.val){
16            left++;
17        }else{
18            left = 0;
19        }
20 
21        if(root.right !=null && root.val == root.right.val){
22            right++;
23        }else{
24            right = 0;
25        }
26 
27        res = Math.max(res, left+right);
28        return Math.max(left, right);
29 
30     }
31 }

 

543. 二叉树的直径

 1 class Solution {
 2     int res = 0;
 3     public int diameterOfBinaryTree(TreeNode root) {
 4         maxPath(root);
 5         return res;
 6     }
 7 
 8     int maxPath(TreeNode root){
 9         // 这里递归结束条件要特别注意:不能是!root(而且不需要判断root为空,因为只有非空才会进入递归),因为单个节点路径长也是0
10         if (root.left == null && root.right == null)  
11             return 0;
12         int left = root.left == null ? 0 : maxPath(root.left) +1;
13         int right = root.right == null ? 0: maxPath(root.right)+1;
14         res = Math.max(res, left+right);
15         return Math.max(right, left); 
16     }
17 }

 

posted @ 2022-09-07 11:41  r1-12king  阅读(156)  评论(0编辑  收藏  举报