力扣日记12.3-【二叉树篇】二叉树的所有路径

力扣日记:【二叉树篇】二叉树的所有路径

日期:2023.12.3
参考:代码随想录、力扣

257. 二叉树的所有路径

题目描述

难度:简单

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

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

示例 1:
力扣日记12.3-【二叉树篇】二叉树的所有路径_第1张图片

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

示例 2:

输入:root = [1]
输出:[“1”]

提示:

  • 树中节点的数目在范围 [1, 100] 内
  • -100 <= Node.val <= 100

题解

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
#define SOLUTION 2
public:
#if SOLUTION == 1
    // 本体关键在于 递归+回溯
    // 如对于[1,2,3,null,5]这样的二叉树, 首先遍历了1-2-5后, 需要回溯到1-2, 再回溯到1, 再继续遍历1-3
    // 因此需要在每次递归后进行回溯(见代码)
    // 至于遍历, 由于是从根节点遍历到叶子节点, 因此使用前序遍历(中-左-右)
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<int> path;
        vector<string> result;
        if (root == NULL) return result;
        traversal(root, path, result);
        return result;
    }
    // 递归参数与返回值:参数为当前节点,当前路径以及结果集;返回值为空
    void traversal(TreeNode* node, vector<int>& path, vector<string>& result) { // 注意是引用传递
        // 递归处理(这里中节点的处理要放在终止条件前,因为终止条件需返回包含叶子节点的路径)
        path.push_back(node->val);
        // 递归终止条件:当遇到叶子节点时,将path放入结果集并返回
        if (node->left == nullptr && node->right == nullptr) {
            // 将path里记录的路径转为string格式
            string sPath;
            for (int i = 0; i < path.size() - 1; i++) { 
                sPath += to_string(path[i]);
                sPath += "->";
            }
            sPath += to_string(path[path.size() - 1]); // 记录最后一个节点(叶子节点)
            result.push_back(sPath); // 收集一个路径
            return;
        }
        // 左节点
        if (node->left) {   // 防止叶子节点判断时为空
            traversal(node->left, path, result);
            path.pop_back();    // 回溯(遍历到叶子节点或左右节点都遍历了, 则要回溯到上一个节点)
        }
        if (node->right) {
            traversal(node->right, path, result);
            path.pop_back();    // 同理
        }
    }

#elif SOLUTION == 2 // 精简版
    vector<string> binaryTreePaths(TreeNode* root) {
        string path;
        vector<string> result;
        if (root == NULL) return result;
        traversal(root, path, result);
        return result;
    }
    // 这里直接用string,避免了vector到string的转换
    void traversal(TreeNode* node, string path, vector<string> &result) {
        // 中
        path += to_string(node->val);
        // 终止条件
        if (node->left == nullptr && node->right == nullptr) {
            result.push_back(path);
            return;
        }
        // 递归处理
        if (node->left) {
            // 注意这里的参数为 path + "->", 而不是先将path = path + "->"再传入path
            // 同时string path也不是引用传递
            // 也就是当递归结束后, path 仍为原来的path, 而不是 path + "->",不用显式回溯"->"
            // 更不是完整路径(如果是引用传递则是如上面的完整路径),因此也不用显式回溯元素值
            traversal(node->left, path + "->", result);
        }
        if (node->right) {
            traversal(node->right, path + "->", result);
        }
    }
#endif
};

复杂度

时间复杂度:
空间复杂度:

思路总结

  • 本题关键在于 递归+回溯
    • 如对于[1,2,3,null,5]这样的二叉树, 首先遍历了1-2-5后, 需要回溯到1-2, 再回溯到1, 再继续遍历1-3
    • 因此需要在每次递归后进行回溯(见代码)
  • 至于遍历, 由于是从根节点遍历到叶子节点, 因此使用前序遍历(中-左-右)
  • 前序遍历以及回溯的过程示意图
    力扣日记12.3-【二叉树篇】二叉树的所有路径_第2张图片
  • 在精简版代码中,之所以不用主动显式回溯
    • 是因为在递归函数的参数传递时,参数为 path + “->”, 而不是先将path = path + "->"再传入path
    • 同时string path也不是引用传递
    • 也就是当递归结束后, path 仍为原来的path, 而不是 path + “->”,不用显式回溯"->"
    • 更不是完整路径(如果是引用传递则是如上面的完整路径),因此也不用显式回溯元素值
  • 注意本次递归的中节点处理要放在终止条件之前,因为在终止条件时要存入包含此中节点的完整路径
  • 关于本题的迭代法,目前还未学习…二刷再补充

你可能感兴趣的:(leetcode,算法,职场和发展)