二叉树——二叉树的层序遍历
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
这是经典的二叉树层序遍历(BFS),用队列实现。
思路
- 使用Queue保存每一层节点
- 每次先获取当前队列大小,代表这一层右多少节点
- 依次出队、收集值,再把左右孩子入队
- 一层收集完,加入结果集合
Java完整代码实现如下:
import java.util.*;
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode() {}
TreeNode(int val) { this.val = val; }
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
class Solution {
public List<List
List<List
if (root == null) return res;
Queue
queue.offer(root);
while (!queue.isEmpty()) {
// 当前层的节点个数
int levelSize = queue.size();
List
for (int i = 0; i < levelSize; i++) {
TreeNode node = queue.poll();
level.add(node.val);
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
res.add(level);
}
return res;
}
}
复杂度:
- 时间:O(n) 每个节点入队出队各一次
- 空间:**O(n) **队列最多存一层节点(满二叉树最后一层约 n/2)
代码解释:
- ArrayList:动态数组
- LinkedList:双向链表
它们都实现了List接口,用法几乎一样,但底层结构完全不同,性能天差地别
1.ArrayList(动态数组)
- 底层是数组
- 数组特点:连续内存、支持随机访问
- 初始化右容量,满了会自动扩容(复制到新数组)
优点
- 查找/访问极快:get(i) 时间复杂度 O(1)
- 遍历速度快
缺点
- 插入、删除慢:中间插入要移动后面所有元素
- 扩容有开销
什么时候用?
读多写少、频繁查找、遍历
比如:存储列表数据、展示数据、层序遍历存结果
2.LinkedList(双向链表)
- 底层是双向链表
- 每个节点存:前节点、值、后节点
- 内存不连续
优点
- 插入、删除极快:只改指针,不用移动元素
- 不需要扩容
缺点
- 查找慢:要从头/尾遍历,get(i) 复杂度 O(n)
- 占用内存更大(每个节点多存前后指针)
什么时候用?
频繁增删、尤其是头尾操作
比如:队列、栈、LRU缓存
回到代码中来
1.Queue
这里必须用LinkedList,因为:
- Queue接口需要快速入队、出队(头尾操作)
- ArrayList 做队列性能极差
2.List<List
存结果用动态数组,因为只添加、不删、最后要遍历输出,ArrayList 最快。
总结:
- ArrayList = 数组:查得快,增删慢
- LinkedList = 链表:增删快,查得慢
queue.offer(root)是什么?
- offer:入队、添加到队列尾部
- 意思:把根节点放进队列
为什么用 offer 而不是 add?
Queue 队列接口推荐用:
- offer(e):添加元素,成功返回 true
- poll():取出并删除队首元素
- peek():看一眼队首但不删
它们比 add/remove 更安全,不会抛异常。
浙公网安备 33010602011771号