如何求二叉树从根节点到叶子节点的所有路径

409 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第30天,点击查看活动详情 >>

如何求二叉树从根节点到叶子节点的所有路径

前面两次我们分别学习了怎样去求二叉树的最大最小深度和完全二叉树的结点个数,今天我们来学习一下如何去求二叉树从根节点到叶子结点的所有路径;并按照如下格式进行返回:

["1->2->5","1->3"]

算法分析

要求二叉树从根节点到叶子结点的所有路径,本质上是一个遍历问题(遍历边),和前面学习的二叉树的遍历有点相似,只不过一个是遍历点,一个是遍历边。

很自然而然,我们就会想到一边遍历树的结点,一边将节点加入路径中即可(因为是要按照固定格式返回的,所以在这的过程中还涉及到了一个字符串拼接的问题)。

递归遍历求解

递归终止条件:

// 叶子结点
if (root.left == null && root.right == null) {
    /**
     * 下面这里考察的主要点就是对字符串的拼接了
     * 如何才能让字符串和递归函数形成一定的规律
     */
    // 输出
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < routes.size() - 1; i++) {
        sb.append(routes.get(i)).append("->");
    }
    sb.append(routes.get(routes.size() - 1));
    result.add(sb.toString());
    return;
}

虽然说上面的代码展示的是递归的终止条件,但是它里面还包含了一些比较重要的逻辑:

  1. 对叶子结点的处理:遇到叶子结点如何将其加入原本的路径当中
  2. 字符串拼接处理:其实这是核心,这里要考虑的就是如何拼接字符串才能在符合递归顺序的前提下,又满足题目要求的返回格式

处理左节点:

(前面已经处理过了叶子结点了)

其实这里就涉及到了一个回溯的过程,因为在递归的过程中节点是不断地加入的,而左边和右边的结点是不应该同时出现在一个路径当中的,所以此处要用到回溯,去除递归过程中左右节点的相互影响。

if (root.left != null) {
    traversal(root.left, routes, result);
    /**
     *前面处理时,会增加左子节点,而在下面处理右边时是不需要左子节点的
     * 所以需要回溯,也就是将左子节点去掉,再去处理右边
     */
    routes.remove(routes.size() - 1);// 回溯到根节点
}

处理右节点:

处理右节点的处理逻辑和处理左节点时是非常相似的,思想是一样的,都用到了回溯,避免了左右两边的相互干扰。

if (root.right != null) {
    recursion(root.right, routes, result);
    /**
     *前面处理时,会增加右子节点,而在下面处理右边时是不需要右子节点的
     * 所以需要回溯,也就是将右子节点去掉,再去处理左边
     */
    routes.remove(routes.size() - 1);// 回溯到根节点
}

具体代码实现:

经过了以上的具体分析,相信大家都可以很容易地写出完整的求解代码。

调用函数如下:

        public List<String> binaryTreePaths(TreeNode root) {
            List<String> result = new ArrayList<>();
            // 根节点为空,返回空 list
            if (root == null) {
                return result;
            }
            List<Integer> routes = new ArrayList<>();
            recursion(root, routes, result);
            return result;
        }

递归函数如下:

    private void recursion(TreeNode root, List<Integer> routes, List<String> result) {
            // 加入根节点
            routes.add(root.val);
            // 叶子结点
            if (root.left == null && root.right == null) {
                /**
                 * 下面这里考察的主要点就是对字符串的拼接了
                 * 如何才能让字符串和递归函数形成一定的规律
                 */
                // 输出
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < routes.size() - 1; i++) {
                    sb.append(routes.get(i)).append("->");
                }
                sb.append(routes.get(routes.size() - 1));
                result.add(sb.toString());
                return;
            }
            if (root.left != null) {
                recursion(root.left, routes, result);
                /**
                 *前面处理时,会增加左子节点,而在下面处理右边时是不需要左子节点的
                 * 所以需要回溯,也就是将左子节点去掉,再去处理右边
                 */
                routes.remove(routes.size() - 1);// 回溯到根节点
            }
            if (root.right != null) {
                recursion(root.right, routes, result);
                /**
                 *前面处理时,会增加右子节点,而在下面处理右边时是不需要右子节点的
                 * 所以需要回溯,也就是将右子节点去掉,再去处理左边
                 */
                routes.remove(routes.size() - 1);// 回溯到根节点
            }
        }

总结:

这次我们学习了如何去求解二叉树从根节点到叶子结点的所有路径。可能有些朋友对这个问法有点陌生,但是只要认真去理解了,就很容易求解出来了。

本质上就是利用二叉树的遍历不断加入要处理的结点,再根据拼接字符串来实现按指定格式返回各种路径。