代码随想录:二叉树13

文章介绍了如何使用前序递归和迭代的方式在二叉树中找到所有从根节点到叶子节点的路径,详细展示了两种方法的代码实现以及它们的逻辑思路。
摘要由CSDN通过智能技术生成

目录

257.二叉树的所有路径

题目

代码(前序递归回溯)

代码(前序迭代)

总结


257.二叉树的所有路径

题目

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

叶子节点 是指没有子节点的节点。

示例 1:

输入:root = [1,2,3,null,5]
输出:["1->2->5","1->3"]

代码(前序递归回溯)

class Solution {
    public List<String> binaryTreePaths(TreeNode root) {
        List<String> result = new ArrayList<>(); //存放路径合集
        List<Integer> path = new ArrayList<>(); //存放单条路径

        if(root == null){
            return result;
        }
        traverse(root,path,result);
        return result;

    }
    //前序递归遍历二叉树
    public void traverse(TreeNode cur,List<Integer> path,List<String> result){
        
        //处理中间节点,这里中间节点的处理必须写到终止条件前面(只有一个节点)
        path.add(cur.val);
        
        //终止条件,遍历到叶子节点
        if(cur.left == null && cur.right == null){
            //先处理list的path为格式化的String
            StringBuilder sb = new StringBuilder();
            //遍历path的每个节点值
            for(int i=0; i < path.size();i++){
                sb.append(path.get(i));  //加入节点值
                if(i != path.size() - 1){
                    sb.append("->");  //不是最后一个节点,加入->
                }
            }
            //再把String添加到result中
            result.add(sb.toString());
            //终止
            return;
        }

        //单层逻辑,继续处理左右孩子
        if(cur.left != null){
            //递归:当前节点有左孩子,递归左孩子的路径
            traverse(cur.left,path,result);
            //回溯:由于path存储的是单条路径,递归左孩子后需要把左孩子从path中去掉
            path.remove(path.size() - 1); 
        }
        if(cur.right != null){
            //递归:当前节点有右孩子,递归右孩子的路径
            traverse(cur.right,path,result);
            //回溯:由于path存储的是单条路径,递归右孩子后需要把右孩子从path中去掉
            path.remove(path.size() - 1); 
        }
    }
}

代码(前序迭代)

class Solution {
    public List<String> binaryTreePaths(TreeNode root) {
        List<String> result = new ArrayList<>(); //存放路径合集
        Stack<TreeNode> stacknode = new Stack<>();  //存放前序遍历的节点
        //stackpath的设置就是在原先前序遍历记录节点的基础上,把从根节点到当前遍历节点的路径的存储下来
        Stack<String> stackpath = new Stack<>();  //存放前序遍历的路径

        if(root == null){
            return result;
        }
        //两个栈是一一对应的,node栈存储的是前序的节点,path栈存储的是每个节点对应的从根节点到它本身的路径
        //根节点入队
        stacknode.push(root);
        //根节点路径入队
        stackpath.push(root.val+""); //+""是为了把int值转为String

        while(!stacknode.isEmpty()){
            TreeNode cur = stacknode.pop(); //中间节点cur出队
            String path = stackpath.pop(); //中间节点的路径出队,path对应的是根节点到cur的路径

            //如果cur是叶子节点,path正好是到cur叶子节点的一条路径
            if(cur.left == null && cur.right == null){
                result.add(path);  //把根节点到cur(叶子结点)的路径入队
            }

            //处理右孩子
            if(cur.right != null){
                stacknode.push(cur.right); //右孩子节点入队
                //path是根节点到cur节点的路径
                //再加上cur.right.val,就是根节点到cur.right的路径
                stackpath.push(path + "->" + cur.right.val); //右孩子路径入队
            }

            //处理左孩子
            if(cur.left != null){
                stacknode.push(cur.left); //左孩子节点入队
                stackpath.push(path + "->" + cur.left.val); //左孩子路径入队
            }
        }
        return result;
    }
}
    

代码(层序迭代)

class Solution {
    public List<String> binaryTreePaths(TreeNode root) {
        Queue<TreeNode> node = new ArrayDeque<>();
        Queue<String> path = new ArrayDeque<>();
        List<String> res = new ArrayList<>();

        if(root == null){
            return res;
        }

        node.offer(root);
        path.offer(root.val+"");

        while(!node.isEmpty()){
            int len = node.size();
            while(len-- > 0){
                TreeNode cur = node.poll();
                String curpath = path.poll();
                if(cur.left == null && cur.right == null){
                    res.add(curpath);
                }
                if(cur.left != null){
                    node.offer(cur.left);
                    StringBuilder sb = new StringBuilder(curpath);
                    sb.append("->");
                    sb.append(cur.left.val);
                    path.offer(sb.toString());
                }
                if(cur.right != null){
                    node.offer(cur.right);
                    StringBuilder sb = new StringBuilder(curpath);
                    sb.append("->");
                    sb.append(cur.right.val);
                    path.offer(sb.toString());
                }
            }
        }
        return res;
    }
}
    

 

总结

1.前序递归回溯

        前序递归回溯的逻辑是,用path存储单条路径,用result存储所有路径。

        先处理中间节点,加入到path中。

        终止条件是遇到叶子节点,就把path转为对应的String并添加到result中。

        单层逻辑是,分别递归回溯处理每个左右孩子。如果左孩子非空,说明路径需要往左孩子走,traverse左孩子,获取左孩子中的路径,由于path中存储的是单条路径,traverse左孩子后,相当于左孩子中所有的路径都已经存到result中,而当前的path中有左孩子的值,需要去掉它,保证path中存储的是中间节点值,然后才能继续递归。如果右孩子非空,原理也是一样的。

2.前序迭代

        前序迭代的逻辑是,用一个node栈存储前序遍历的节点,用一个path栈存储前序遍历的节点对应的路径(根节点到当前遍历节点),这两个栈是一一对应的。当某个节点cur出栈时,其对应的path也要出栈。然后判断cur是否是叶子节点,如果是,就把对应的path添加的result路径集合中。如果不是叶子节点,就要继续遍历,先处理右孩子,当右孩子非空,先把右孩子添加到node栈中,同时,把path+右孩子值的路径添加到path栈中。再处理左孩子,逻辑同上。

        用node和path栈同时前序遍历,可以简单理解为,我们一边遍历节点保存node栈,一边把从根节点到该节点的路径也记录在path栈。这样,当我们要出栈处理节点时,同时可以出栈获取该节点的路径。如果该路径是一个到叶子的路径,就能直接添加到result中。如果该路径不是到叶子的路径,就继续遍历他的孩子,同时把该路径+孩子的路径添加到path栈。

        核心逻辑相当于一边前序遍历node,一边前序存储path。然后当我们按原先前序遍历的逻辑处理node时,加上判断是不是叶子节点,如果是,就把出栈的path存到result中。而看似难理解的stackpath.push(path + "->" + cur.right.val)和stackpath.push(path + "->" + cur.left.val)语句,其实就是在遍历时,同时把当前遍历节点的路径也存到栈里。方便我们在处理node时,同时拿到根节点到node的路径罢了。

第一次接触回溯,有点难理解,不过可以画个图举例,然后也能理解个差不多。今天的题目难一点,就做一道题吧。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

守岁白驹hh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值