分享

464. BFS和DFS解二叉树的所有路径

 数据结构和算法 2023-06-10 发布于上海

You've gotta let go that stuff in the past, cause it just doesn't matter.

过去的事就让它过去,因为那些都无关紧要。

问题描述



给定一个二叉树,返回所有从根节点到叶子节点的路径

说明: 叶子节点是指没有子节点的节点。

示例:

输入:

   1

 /   \

2     3

 \

  5

输出: ["1->2->5", "1->3"]

解释: 所有根节点到叶子节点的路径为:

1->2->5, 1->3

DFS解决



这题让求的是从根节点到叶子节点的所有路径,最常见的一种方式就是DFS(深度优先搜索),也就是从根节点沿着最左边节点一直走下去(如果没有左子节点,有右子节点,会沿着右子节点走下去),当到达叶子节点的时候在返回到父节点,然后沿着父节点的右子节点开始走下去,如下图所示

在前面讲过二叉树的dfs,373,数据结构-6,树,他的代码如下

1public static void treeDFS(TreeNode root{
2    if (root == null)
3        return;
4    System.out.println(root.val);
5    treeDFS(root.left);
6    treeDFS(root.right);
7}

他就是从根节点到叶子节点的所有路径都会访问一遍,我们只需要把这个路径串联起来即可,这里来仿照上面的代码改造一下

 1public List<String> binaryTreePaths(TreeNode root) {
2    List<String> res = new ArrayList<>();
3    if (root == null)
4        return res;
5    dfs(root, "", res);
6    return res;
7}
8
9private void dfs(TreeNode root, String path, List<String> res) {
10    //如果到达叶子节点,就把结果存放到集合res中
11    if (root.left == null && root.right == null)
12        res.add(path + root.val);
13    //如果左子节点不为空,就沿着左子节点走下去
14    if (root.left != null)
15        dfs(root.left, path + root.val + "->", res);
16    //如果右子节点不为空,就沿着右子节点走下去
17    if (root.right != null)
18        dfs(root.right, path + root.val + "->", res);
19}

在第373题讲到二叉树DFS遍历的时候,还有一种非递归的写法,代码如下

 1public static void treeDFS(TreeNode root) {
2    Stack<TreeNode> stack = new Stack<>();
3    stack.add(root);
4    while (!stack.empty()) {
5        TreeNode node = stack.pop();
6        System.out.println(node.val);
7        if (node.right != null) {
8            stack.push(node.right);
9        }
10        if (node.left != null) {
11            stack.push(node.left);
12        }
13    }
14}

因为他的遍历过程没变,只不过写法改变了,我们也可以来对他进行改造,看下最终代码

 1public List<String> binaryTreePaths(TreeNode root) {
2    List<String> res = new ArrayList<>();
3    if (root == null)
4        return res;
5    //存储节点的栈
6    Stack<TreeNode> stackNode = new Stack<>();
7    //存储路径的栈,和上面的栈是同步进行的,这里路径指的是
8    //从根节点到当前节点的路径
9    Stack<String> stackPath = new Stack<>();
10    //根节点和根节点的路径同时入栈
11    stackNode.push(root);
12    stackPath.push(root.val + "");
13    while (!stackNode.empty()) {
14        //当前节点和对应的路径同时出栈
15        TreeNode node = stackNode.pop();
16        String path = stackPath.pop();
17        //如果到达叶子节点,就把路径加入到集合res中
18        if (node.left == null && node.right == null) {
19            res.add(path);
20        }
21        //如果右子节不为空,就把右子节点和对应的路径分别加入到栈中
22        if (node.right != null) {
23            stackPath.push(path + "->" + node.right.val);
24            stackNode.push(node.right);
25        }
26        //同上
27        if (node.left != null) {
28            stackPath.push(path + "->" + node.left.val);
29            stackNode.push(node.left);
30        }
31    }
32    return res;
33}

BFS解决



BFS就是一层一层的打印,如下图所示

只需要使用一个队列,把每层的节点都存放到队列中,然后再一个个出队,顺便把子节点在一个个存放到队列中……一直这样循环下去,直到队列为空为止,在373,数据结构-6,树的时候也提到过二叉树的BFS遍历,他的代码如下

 1public void levelOrder(TreeNode tree{
2    if (tree == null)
3        return;
4    Queue<TreeNode> queue = new LinkedList<>();
5    queue.add(tree);//相当于把数据加入到队列尾部
6    while (!queue.isEmpty()) {
7        //poll方法相当于移除队列头部的元素
8        TreeNode node = queue.poll();
9        System.out.println(node.val);
10        if (node.left != null)
11            queue.add(node.left);
12        if (node.right != null)
13            queue.add(node.right);
14    }
15}

也可以对上面的代码进行改造,改造的原理和DFS的非递归解法一样,就是使用一个变量存放从根节点到当前节点的路径,代码如下

 1public List<String> binaryTreePaths(TreeNode root{
2    List<String> res = new ArrayList<>();
3    if (root == null)
4        return res;
5    //队列,节点和路径成对出现,路径就是从根节点到当前节点的路径
6    Queue<Object> queue = new LinkedList<>();
7    queue.add(root);
8    queue.add(root.val + "");
9    while (!queue.isEmpty()) {
10        TreeNode node = (TreeNode) queue.poll();
11        String path = (String) queue.poll();
12        //如果到叶子节点,说明找到了一条完整路径
13        if (node.left == null && node.right == null) {
14            res.add(path);
15        }
16
17        //右子节点不为空就把右子节点和路径存放到队列中
18        if (node.right != null) {
19            queue.add(node.right);
20            queue.add(path + "->" + node.right.val);
21        }
22
23        //左子节点不为空就把左子节点和路径存放到队列中
24        if (node.left != null) {
25            queue.add(node.left);
26            queue.add(path + "->" + node.left.val);
27        }
28    }
29    return res;
30}

递归解法



我们来思考这样一个问题,如果知道了左子树和右子树的所有路径,我们在用根节点和他们连在一起,是不是就是从根节点到所有叶子节点的所有路径,所以这里最容易想到的就是递归

最后再来看下代码

 1public List<String> binaryTreePaths(TreeNode root) {
2    List<String> res = new ArrayList<>();
3    if (root == null)
4        return res;
5    //到达叶子节点,把路径加入到集合中
6    if (root.left == null && root.right == null) {
7        res.add(root.val + "");
8        return res;
9    }
10    //遍历左子节点的所有路径
11    for (String path : binaryTreePaths(root.left)) {
12        res.add(root.val + "->" + path);
13    }
14    //遍历右子节点的所有路径
15    for (String path : binaryTreePaths(root.right)) {
16        res.add(root.val + "->" + path);
17    }
18    return res;
19}

总结



二叉树的遍历常见的也就是前序遍历,中序遍历,后续遍历,BFS,DFS,以及莫里斯的前序,中序和后续这几种,有的还说前序遍历就是DFS,其实可以这么说。但DFS却不是前序遍历,因为DFS不光可以先从左子树开始遍历还可以先从右子树开始遍历。前面五种之前在373,数据结构-6,树已经讲过,后面的3种后续有时间会再做介绍。只要掌握二叉树的这几种遍历方式,对于大部分关于二叉树的问题都可以参照这几种方式进行修改来解决。

456,解二叉树的右视图的两种方式

444,二叉树的序列化与反序列化

442,剑指 Offer-回溯算法解二叉树中和为某一值的路径

372,二叉树的最近公共祖先

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章