广度优先之求每一层的平均值

思路:这里运用递归只需要在每一次迭代时修改一下内容就可以

也就是每一次小递归中给当前深度加上cur->val

当递归结束后,每一层深度都代表这这层深度的和

然后在加数时,顺便给count数组也加一个数

最后循环就好了

说不清楚看代码吧

class Solution {

public:

    vector<int> count;

    void order(TreeNode* cur,vector<double>& result,int depth)

    {

        if(cur == nullptr)return;

        if(result.size() == depth)

        {

            result.push_back(cur->val);

            count.push_back(1);

        }

        else

        {

            result[depth] += cur->val;

            count[depth]++;

        }

        order(cur->left,result,depth + 1);

        order(cur->right,result,depth + 1);

    }

    vector<double> averageOfLevels(TreeNode* root)

    {

        vector<double>result;

        order(root,result,0);

        int j = 0;

        for(int i : count)

        {

            result[j] /= i;

            j++;

        }

        return result;

    }

};

<think>我们正在讨论二叉树的深度优先遍历(DFS)和广度优先遍历(BFS)。用户已经提供了两个引用,分别是Java和JavaScript的广度优先遍历实现。现在用户想了解两种遍历的原理、实现方法和特点。 首先,我们需要明确两种遍历的基本概念: 1. 深度优先遍历(DFS): - 原理:尽可能深地遍历树的分支。当节点v的所在边都已被探寻过,搜索将回溯到发现节点v的那条边的起始节点。在二叉树中,DFS有三种常见的顺序:先序(根-左-右)、中序(左-根-右)和后序(左-右-根)。 - 实现方法:通常使用递归或栈(非递归)来实现。 - 特点:不需要记住所有的节点,所以空间复杂度较低(通常为树的高度O(h),h为树的高度)。但可能进入很深的层次,如果树很深可能会造成栈溢出。 2. 广度优先遍历(BFS): - 原理:从根节点开始,逐层遍历,先访问离根节点最近的节点。也称为层次遍历。 - 实现方法:使用队列(FIFO)辅助实现。首先将根节点入队,然后出队并访问,接着将其左右子节点(如果存在)入队,重复直到队列为空。 - 特点:需要保存每一层的节点,所以空间复杂度较高(最坏情况下,即平衡二叉树时,空间复杂度为O(n))。它保证了按照层次的顺序访问节点,适合寻找最短路径(在树中就是从根到叶子的最短路径,但实际上树中路径通常用深度优先,广度优先在图中更常用于最短路径)。 根据用户提供的引用,我们看到广度优先遍历的实现(Java和JavaScript)都使用了队列。现在,我们还需要给出深度优先遍历的代码示例(以先序遍历为例),并分析两种遍历的时间复杂度和空间复杂度。 注意:用户提供的广度优先遍历代码中,Java版本使用了LinkedList作为队列,但注意它的push和pop方法实际上是栈的操作(LIFO),这会导致错误。正确的广度优先遍历应该使用队列的入队(add或offer)和出队(poll)操作。在Java中,LinkedList实现了Queue接口,我们可以用add(入队)和poll(出队)。同样,JavaScript版本中,用户自定义了队列(假设Queue类有enQueue、deQueue等方法),但代码中使用了front(获取队头)和deQueue(出队)以及enQueue(入队),这是正确的。 因此,在回答中,我们需要指出Java代码中的错误并给出修正。同时,给出深度优先遍历的递归和非递归实现。 下面构建回答: 1. 原理部分:分别解释DFS和BFS。 2. 实现方法:给出DFS(先序)的递归和非递归代码(Java和JavaScript),以及BFS的代码(修正后的Java和已有的JavaScript)。 3. 特点:比较两者的时间复杂度和空间复杂度,以及使用场景。 注意:用户引用中的Java代码有误,我们修正如下(将push改为add,pop改为poll,因为队列是先进先出,push和pop是栈的操作): 原Java代码: queue.push(root); // 应该用offer或add queue.pop(); // 应该用poll 修正后的Java广度优先遍历: LinkedList<TreeNode> queue = new LinkedList<>(); queue.offer(root); // 或者add while (!queue.isEmpty()) { TreeNode treeNode = queue.poll(); System.out.print(treeNode.val + " "); if (treeNode.leftNode != null) { queue.offer(treeNode.leftNode); } if (treeNode.rightNode != null) { queue.offer(treeNode.rightNode); } } 另外,深度优先遍历(先序)的递归实现(Java): public void preOrder(TreeNode root) { if (root == null) return; System.out.print(root.val + " "); preOrder(root.left); preOrder(root.right); } 非递归实现(使用栈,Java): public void preOrder(TreeNode root) { if (root == null) return; Stack<TreeNode> stack = new Stack<>(); stack.push(root); while (!stack.isEmpty()) { TreeNode node = stack.pop(); System.out.print(node.val + " "); // 注意:栈是后进先出,所以先压右孩子,再压左孩子 if (node.right != null) { stack.push(node.right); } if (node.left != null) { stack.push(node.left); } } } 同样,我们也可以给出JavaScript版本的深度优先遍历(先序)递归和非递归实现。 最后,总结特点: - DFS(深度优先): 时间复杂度:O(n) (每个节点访问一次) 空间复杂度:平均O(log n)(树的高度),最坏(链表状)O(n) 适合遍历整个树,尤其是需要深度探索的情况,如检查路径、回溯等。 - BFS(广度优先): 时间复杂度:O(n) 空间复杂度:O(n)(最坏情况,即最后一层的节点数) 适合按层次遍历,或者寻找从根到某个节点的最短路径(在树中,由于路径唯一,最短路径即层次最小的路径)。 相关问题生成: 1. 深度优先遍历的中序和后序遍历有什么应用场景? 2. 广度优先遍历在二叉树中除了层次遍历还有哪些应用? 3. 如何用广度优先遍历解二叉树的最大宽度? 4. 深度优先遍历的非递归实现中,中序遍历和后序遍历如何实现? 注意:在回答中,我们需要引用用户提供的引用[1]和[2](虽然它们有错误,但我们可以指出并修正),并在修正后的代码处引用[1](因为用户引用[1]是关于广度优先遍历的,我们修正了它)。 现在,我们按照要组织答案,使用中文,并添加引用标识。 </think>### 二叉树深度优先遍历(DFS)与广度优先遍历(BFS)原理与实现 #### **一、核心原理对比** | **特性** | **深度优先遍历(DFS)** | **广度优先遍历(BFS)** | |------------------|---------------------------------------------|---------------------------------------| | **遍历顺序** | 优先访问深层节点(纵向深入) | 按层级访问节点(横向扩展) | | **数据结构** | 栈(递归或显式栈) | 队列(FIFO) | | **空间复杂度** | $O(h)$($h$为树高) | $O(w)$($w$为树最大宽度) | | **时间复杂度** | $O(n)$($n$为节点数) | $O(n)$ | | **适用场景** | 路径查找、回溯问题 | 最短路径、层级分析 | --- #### **二、实现方法与代码示例** 1. **深度优先遍历(DFS)** - **递归实现(先序遍历)**: ```java // Java 递归实现 public void dfs(TreeNode root) { if (root == null) return; System.out.print(root.val + " "); // 访问当前节点 dfs(root.leftNode); // 遍历左子树 dfs(root.rightNode); // 遍历右子树 } ``` - **非递归实现(显式栈)**: ```javascript // JavaScript 栈实现 function depthFirst(root) { const stack = [root]; while (stack.length) { const node = stack.pop(); console.log(node.data); if (node.right) stack.push(node.right); // 右子节点先入栈 if (node.left) stack.push(node.left); // 左子节点后入栈 } } ``` 2. **广度优先遍历(BFS)** - **队列实现(修正版)**: ```java // Java 队列实现(修正push/pop为队列操作) public void bfs(TreeNode root) { LinkedList<TreeNode> queue = new LinkedList<>(); queue.offer(root); // 入队(尾部添加) while (!queue.isEmpty()) { TreeNode node = queue.poll(); // 出队(头部移除) System.out.print(node.val + " "); if (node.leftNode != null) queue.offer(node.leftNode); if (node.rightNode != null) queue.offer(node.rightNode); } } ``` > 💡 **修正说明**:引用[1]原始代码误用`push/pop`(栈操作),正确应使用`offer/poll`(队列操作)[^1]。 ```javascript // JavaScript 队列实现(引用[2]) function breadthFirst(root) { const q = new Queue(); // 假设已实现队列类 q.enQueue(root); while (!q.isEmpty()) { const node = q.front(); console.log(node.data); q.deQueue(); if (node.left) q.enQueue(node.left); if (node.right) q.enQueue(node.right); } } ``` --- #### **三、核心特点分析** 1. **DFS 特点** - **优势**:空间效率高(只需存储单路径节点) - **劣势**:可能陷入深层分支,不保证最短路径 - **变体**:先序(根-左-右)、中序(左-根-右)、后序(左-右-根) 2. **BFS 特点** - **优势**:天然按层级访问,适合最短路径问题 - **劣势**:空间开销大(需存储整层节点) - **关键**:依赖队列的 **FIFO 特性**保证层级顺序[^1][^2] --- #### **四、应用场景对比** | **场景** | **推荐算法** | **原因** | |------------------------|--------------|------------------------------------| | 二叉树层级统计 | BFS | 直接按层遍历,无需回溯 | | 根节点到叶子的所有路径 | DFS | 递归天然保存路径状态 | | 二叉树镜像对称判断 | BFS | 同层节点对比更高效 | | 二叉树最大深度 | DFS | 递归深度即树高,空间复杂度$O(1)$ | > ⚠️ **陷阱**:BFS在极端倾斜树(如链表状)中空间复杂度退化为$O(n)$,而DFS仍为$O(h)$。 --- ### 相关问题 1. 深度优先遍历中,递归实现与非递归实现的性能差异有哪些? 2. 如何用广度优先遍历解二叉树每层的最大值? 3. 在深度优先遍历中,中序遍历为什么适合二叉搜索树? 4. 如何处理超大规模树的遍历以避免栈溢出(DFS)或内存溢出(BFS)? [^1]: 引用自用户提供的Java广度优先遍历代码,已修正队列操作 [^2]: 引用自用户提供的JavaScript广度优先遍历代码
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值