二叉树路径问题

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

1. 自顶向下: 顾名思义,就是从某一个节点(不一定是根节点),从上向下寻找路径,到某一个节点(不一定是叶节点)结束 具体题目如下:

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

解题模板 这类题通常用深度优先搜索(DFS)和广度优先搜索(BFS)解决,BFS较DFS繁琐,这里为了简洁只展现DFS代码 下面是我对两类题目的分析与模板

一、自顶而下: dfs

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

//给定和的路径:
void dfs(TreeNode root, int sum, List<Integer> path)
{
    if (root==null) return;
    sum -= root.val;
    path.add(root.val);
    if (root.left==null && root.right==null && sum == 0)
    {
        res.add(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,不断比较更新全局变量即可

int res=0;
int maxPath(TreeNode root) //以root为路径起始点的最长路径
{
    if (root==null)
        return 0;
    int left=maxPath(root.left);
    int right=maxPath(root.right);
    res = Math.max(res, left + right + root.val); //更新全局变量  
    return Math.max(left, right);   //返回左右路径较长者
}

这类题型DFS注意点:

1、left,right代表的含义要根据题目所求设置,比如最长路径、最大路径和等等

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

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

题目分析 下面是对具体题目的分析和代码呈现 一、自顶向下 257. 二叉树的所有路径 直接套用模板1即可,注意把"->"放在递归调用中

List<string> res;
List<string> binaryTreePaths(TreeNode<T> root)
{
    dfs(root, "");
    return res;
}

void dfs(TreeNode root, string path)
{
    if (root==null)
        return;
    path += String.valueOf(root.val);
    if (root.left==null && root.right==null)
    {
        res.add(path);
        return;
    }
    dfs(root.left, path+"->");
    dfs(root.right, path+"->");
}

113. 路径总和 II 直接套用模板2

 List<List<Integer>> res=new ArrayList<>();
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        Deque<Integer> path=new LinkedList<>();
        dfs(root,targetSum,path);
        return res;
    }
    void dfs(TreeNode node,int sum,Deque<Integer> path){
        if(node==null) return;
        path.offerLast(node.val);
        sum-=node.val;
        if(node.right==null&&node.left==null&&sum==0){
            res.add(new LinkedList<Integer>(path));
        }
        dfs(node.left,sum,path);
        dfs(node.right,sum,path);
        path.pollLast();
    }

437. 路径总和 III 双重递归:先调用dfs函数从root开始查找路径,再调用pathsum函数到root左右子树开始查找 套用模板2

int count = 0;
int pathSum(TreeNode root, int targetSum)
{
    if (root==null)
        return 0;
    dfs(root, targetSum);            //以root为起始点查找路径
    pathSum(root.left, targetSum);  //左子树递归
    pathSum(root.right, targetSum); //右子树递归
    return count;
}

void dfs(TreeNode root, long sum)
{
    if (root==null)
        return;
    sum -= root.val;
    if (sum == 0) //注意不要return,因为不要求到叶节点结束,所以一条路径下面还可能有另一条
        count++;  //如果找到了一个路径全局变量就+1
    dfs(root.left, sum);
    dfs(root.right, sum);
}

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

 List<String> path=new ArrayList<>();
    public String smallestFromLeaf(TreeNode root) {
        dfs(root, "");
        Collections.sort(path); //升序排序
        return path.get(0);
    }
    void dfs(TreeNode root, String s)
{
    if (root==null)
        return;
   s+=((char)('a'+root.val));
    if (root.left==null&&root.right==null){
        String reverse = new StringBuffer(s).reverse().toString();
        path.add(reverse);
        return;
    }
    dfs(root.left, s);
    dfs(root.right, s);
}

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

int res = Integer.MIN_VALUE;
    public int maxPathSum(TreeNode root) {
        maxPath(root);
        return res;
    }
    int maxPath(TreeNode root){
//以root为起点最长路径
        if(root==null) return 0;
        int left=Math.max(maxPath(root.left),0);
        int right=Math.max(maxPath(root.right),0);
        res=Math.max(res,left+right+root.val);
        return Math.max(left+root.val,right+root.val);
    }

687. 最长同值路径

    int res;
    public int longestUnivaluePath(TreeNode root) {
        Path(root);
        return res;
    }
    private int Path(TreeNode root){
        if(root==null) return 0;
        int left=Path(root.left);
        int right=Path(root.right);
        if(root.left!=null&&root.val==root.left.val){
            left++;
        }else{
            left=0;
        }
        if(root.right!=null&&root.val==root.right.val){
            right++;
        }else{
            right=0;
        }
        res=Math.max(res,left+right);
        return Math.max(left,right); 
    }

543. 二叉树的直径

    int res;
    public int diameterOfBinaryTree(TreeNode root) {
        diameter(root);
        return res;
    }
    public int diameter(TreeNode root){
        if(root==null) return 0;
        int left=diameter(root.left);
        int right=diameter(root.right);
        res=Math.max(res,left+right);
        return  Math.max(left,right)+1;
    }

112.路径总和

DFS

首先是 DFS 解法,该解法的想法是一直向下找到 叶子节点,如果到 叶子节点 时 sum == 0,说明找到了一条符合要求的路径。

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

437. 路径总和 III 双重递归:先调用dfs函数从root开始查找路径,再调用pathsum函数到root左右子树开始查找 套用模板2

回溯

这里的回溯指 利用 DFS 找出从根节点到叶子节点的所有路径,只要有任意一条路径的 和 等于 sum,就返回 True

    public boolean hasPathSum(TreeNode root, int targetSum) {
        return dfs(0, targetSum, root);
    }

    public boolean dfs(int sum, int target, TreeNode root){
        if (root==null){
            return false;
        }
        if (root.left==null && root.right == null) return target==sum+root.val;
        boolean dfsleft = false;
        if (root.left!=null){
            dfsleft = dfs(sum+root.val, target, root.left);
        }
        boolean dfsright = false;
        if (!dfsleft && root.right != null ){
            dfsright = dfs(sum+root.val, target, root.right);
        }
        // 并且代表遍历到根节点
        return dfsleft || dfsright;
    }

BFS

BFS 使用 队列 保存遍历到每个节点时的路径和,如果该节点恰好是叶子节点,并且 路径和 正好等于 sum,说明找到了解。

 public boolean hasPathSum(TreeNode root, int targetSum) {
        if (root==null) return false;
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        LinkedList<Integer> sumQueue = new LinkedList<>();
        sumQueue.offer(0);
        while (queue.size()>0){
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                TreeNode poll = queue.poll();
                Integer sum = sumQueue.poll();
                if (poll.left==null && poll.right==null && (poll.val+sum) == targetSum){
                    return true;
                }
                if (poll.left!=null){
                    queue.offer(poll.left);
                    sumQueue.offer(sum+poll.val);
                }
                if (poll.right!=null){
                    queue.offer(poll.right);
                    sumQueue.offer(sum+poll.val);
                }
            }
        }
        return false;
    }

栈 

class solution {
    public boolean haspathsum(treenode root, int targetsum) {
        if(root == null) return false;
        stack<treenode> stack1 = new stack<>();
        stack<integer> stack2 = new stack<>();
        stack1.push(root);
        stack2.push(root.val);
        while(!stack1.isempty()) {
            int size = stack1.size();

            for(int i = 0; i < size; i++) {
                treenode node = stack1.pop();
                int sum = stack2.pop();

                // 如果该节点是叶子节点了,同时该节点的路径数值等于sum,那么就返回true
                if(node.left == null && node.right == null && sum == targetsum) {
                    return true;
                }
                // 右节点,压进去一个节点的时候,将该节点的路径数值也记录下来
                if(node.right != null){
                    stack1.push(node.right);
                    stack2.push(sum + node.right.val);
                }
                // 左节点,压进去一个节点的时候,将该节点的路径数值也记录下来
                if(node.left != null) {
                    stack1.push(node.left);
                    stack2.push(sum + node.left.val);
                }
            }
        }
        return false;
    }
}

113. 路径总和 II

 

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值