当前位置: 首页 > article >正文

leetcode - 257. 二叉树的所有路径

 

257. 二叉树的所有路径

题目

解决

做法一:深度优先搜索 + 回溯

        深度优先搜索(Depth-First Search, DFS)是一种用于遍历或搜索树或图的算法。这种搜索方式会尽可能深地探索每个分支,直到无法继续深入为止,然后回溯到上一个节点,继续探索其他未访问的分支。

        回溯我的个人理解是回退到之前的状态

        大致想法:就是使用递归,在递归过程中使用 StringBuilder 存储路径上的节点和 箭头指向字符,直到 TreeNode 节点中左子节点 和 右子节点 都为空,将 StringBuilder 中存的依赖路径 加入到 list 中,每当退出递归,就回溯(将加入的 (节点值 + "->") 删除掉)

/**
 * 执行用时分布 5 ms 击败 30.20% 复杂度分析 消耗内存分布 41.53 MB 击败 70.56%
 */
public List<String> binaryTreePaths(TreeNode root) {
    List<String> list = new ArrayList<>();
    StringBuilder path = new StringBuilder();
    path.append(root.val);
    // 递归获取 根节点到 叶子节点的路径
    getPath(root, path, list);
    return list;
}

/**
 * 递归获取 根节点到 叶子节点的路径
 */
public void getPath(TreeNode root, StringBuilder path, List<String> list) {
    // 递归边界就是 叶子节点,碰到叶子节点则递归结束,并将叶子节点的路径加入到结果集
    if (root.left == null && root.right == null){
        list.add(path.toString());
        return;
    }
    if (root.left != null){
        String pathStr = "->" + root.left.val;
        // 将 左子树节点的值加入到 树路径中
        path.append(pathStr);
        // 递归左子树
        getPath(root.left, path, list);
        // 回溯,将当前节点的左节点剔除出 字符串
        path.delete(path.length() - pathStr.length(), path.length());
    }
    if (root.right != null){
        String pathStr = "->" + root.right.val;
        path.append(pathStr);
        getPath(root.right, path, list);
        path.delete(path.length() - pathStr.length(), path.length());
    }
}

优化

        做法上基本是这样,但是时间上呢还是太慢了,根据以往经验发现可以将字符串替换成 栈 这个数据结构,弹出一个节点,可比 StringBuilder 删除来得快,StringBuilder.delete 底层是将整个字符数组拷贝的,相比于 栈 他是先进后出的,从栈顶弹出元素相对较快。

        Deque 参考链接:双端队列 【Deque】-CSDN博客

时间复杂度:O(nlogn);空间复杂度:O(nlogn)

因为遍历到叶子节点之后,还需要遍历 栈,最环的情况是 叶子节点的数量为 n/2,每个路径字符串的长度为 log(n)(因为完全二叉树的高度为 log(n))

/**
 * 执行用时分布 3 ms 击败 33.70% 复杂度分析 消耗内存分布 41.55 MB 击败 64.43%
 */
public List<String> binaryTreePaths(TreeNode root) {
    List<String> list = new ArrayList<>();
    // 双端队列
    Deque<Integer> path = new LinkedList<>();
    // 递归获取 根节点到 叶子节点的路径
    getPath(root, path, list);
    return list;
}

/**
 * 递归获取 根节点到 叶子节点的路径
 */
public void getPath(TreeNode root, Deque<Integer> path, List<String> list) {
    if (root == null){
        return;
    }
    if (root.left == null && root.right == null){
        StringBuilder builder = new StringBuilder();
        // 遍历,组合成路径字符串
        path.forEach(val -> {
            builder.append(val).append("->");
        });
        // 加入当前叶子节点的 值
        builder.append(root.val);
        list.add(builder.toString());
        return;
    }
    // 因为回溯需要将,当前节点弹出并且需要按顺序循环遍历出来,所以选用 尾部插入,尾部弹出
    // 将当前节点加入
    path.add(root.val);
    // 递归左子树
    getPath(root.left, path, list);
    // 递归右子树
    getPath(root.right, path, list);
    // 回溯
    path.removeLast();
}

做法二:深度优先搜索

参考官解:. - 力扣(LeetCode)

这里相对于上面快,是因为这里不需要回溯,使用的是局部变量

/**
 * 参考官解:https://leetcode.cn/problems/binary-tree-paths/solutions/400326/er-cha-shu-de-suo-you-lu-jing-by-leetcode-solution/
 * 深度优先遍历解法
 * 执行用时分布 1 ms 击败 99.56% 复杂度分析 消耗内存分布 41.57 MB 击败 58.89%
 */
public List<String> binaryTreePaths(TreeNode root) {
    List<String> list = new ArrayList<>();
    // 递归获取 根节点到 叶子节点的路径
    getPath(root, "", list);
    return list;
}

/**
 * 递归获取 根节点到 叶子节点的路径
 */
public void getPath(TreeNode root, String path, List<String> list) {
    if (root == null){
        return;
    }
    StringBuilder builder = new StringBuilder(path);
    builder.append(root.val);
    if (root.left == null && root.right == null){
        list.add(builder.toString());
        return;
    }
    builder.append("->");
    // 递归左子树
    getPath(root.left, builder.toString(), list);
    // 递归右子树
    getPath(root.right, builder.toString(), list);
}

做法三:广度优先遍历

广度优先遍历

        广度优先遍历(Breadth-First Search, BFS)是一种用于图或树的遍历算法。这种搜索方式从根节点(或任意一个起始节点)开始,逐层访问每个节点的邻居节点,直到所有可达节点都被访问到。BFS 的特点是先访问离起点最近的节点,然后再逐步向外扩展。

/**
 * 宽度遍历/广度优先遍历
 * @param head
 */
private static void levelOrder(Node head){
    if (head == null) {
        return;
    }
    Queue<Node> queue = new LinkedList<>();
    queue.offer(head);
    while (!queue.isEmpty()){
        head = queue.poll();
        System.out.print(head.value + " ");
        if (head.left != null){
            queue.offer(head.left);
        }
        if (head.right != null){
            queue.offer(head.right);
        }
    }
}

代码

/**
 * 广度优先搜索
 * 执行用时分布 2 ms 击败 47.70% 复杂度分析 消耗内存分布 41.48 MB 击败 79.08%
 */
public List<String> binaryTreePaths(TreeNode root) {
    // 存储结果集
    List<String> result = new ArrayList<>();

    // 用于遍历存储节点
    Queue<TreeNode> nodeQueue = new LinkedList<>();
    // 用于存储 nodeQueue 的依赖路径
    Queue<String> pathQueue = new LinkedList<>();

    // 先将 根节点 加入队列用于遍历
    nodeQueue.add(root);
    // 将路径同步加入
    pathQueue.add(Integer.toString(root.val));

    // 遍历知道所有节点为空
    while (!nodeQueue.isEmpty()){
        // 弹出节点和路径
        TreeNode node = nodeQueue.poll();
        String path = pathQueue.poll();

        // 叶子节点
        if (node.left == null && node.right == null) {
            result.add(path);
            continue;
        }

        // 如果左子节点不为空就将节点加入到队列中,并将依赖路径拼接好加入队列
        if (node.left != null){
            nodeQueue.add(node.left);
            pathQueue.add(new StringBuilder(path).append("->").append(node.left.val).toString());
        }

        if (node.right != null){
            nodeQueue.add(node.right);
            pathQueue.add(new StringBuilder(path).append("->").append(node.right.val).toString());
        }
    }
    return result;
}


http://www.kler.cn/a/371007.html

相关文章:

  • Python 实现 NLP 的完整流程
  • 【漏洞分析】DDOS攻防分析
  • django在线考试系统
  • C++|CRC校验总结
  • 【Vue3 入门到实战】1. 创建Vue3工程
  • React:构建用户界面的JavaScript库
  • 堆的应用——堆排序和TOP-K问题
  • Redis_写时复制(cow)
  • 个人防护装备分割系统:全面扶持小白
  • 了解AIGC——自然语言处理与生成
  • 测试造数,excel转insert语句
  • 医院管理系统设计与实现
  • Vue中使用 vuedraggable进行拖拽
  • JavaScript基本内容续集之函数和对象
  • 服务攻防之开发组件安全
  • 18 Docker容器集群网络架构:一、etcd 概述
  • mit6824-04-主备份复制(VM-FT论文)
  • 小小猫棒onu替换家用光猫,薅运营商带宽羊毛,突破1000M
  • golang 后端验证码 模拟接口 post get ,postman测试
  • 洛谷 P4251 [SCOI2015] 小凸玩矩阵
  • python实战项目49:足彩开奖竞猜数据抓取
  • uni-app如何接收uni.$emit()里面传递的内容
  • 【AI试衣整合包及教程】CatVTON带你进入AI换装新时代
  • SpringBoot实现 License 认证(只校验有效期)
  • 三维测量与建模笔记 - 2.1 坐标转换基础
  • 一款强大的开源OCR工具,支持90+语言识别