LeetCode 103中等二叉树
二叉树的锯齿形层序遍历 图解题解
这道题到底在问什么
给一棵二叉树,按锯齿形层序返回它的节点值:第 1 层从左到右,第 2 层从右到左,第 3 层又从左到右……逐层交替方向。
- 输入
- 上图这棵 12 节点树
- 输出
- [[3],[20,9],[8,10,15,7],[5,6,4,2,1]]
最优解:一步一步想明白
- 3思路一句话:BFS 一点没变,只用一个 flag 控制——偶数层把该层结果 reverse。下面一步步演给你看。
- 4根节点入队。第 1 层方向=左到右。
- 5出队 3:先按出队顺序攒进第 1 层缓冲。
- 63 的孩子 9 入队(孩子永远按左右顺序入队,方向只影响“怎么收”)。
- 73 的孩子 20 入队(孩子永远按左右顺序入队,方向只影响“怎么收”)。 第 1 层(左→右)收完:[3]——队列里现在全是下一层,方向翻转,继续。
- 8出队 9:先按出队顺序攒进第 2 层缓冲。
- 99 的孩子 8 入队(孩子永远按左右顺序入队,方向只影响“怎么收”)。
- 109 的孩子 10 入队(孩子永远按左右顺序入队,方向只影响“怎么收”)。
- 11出队 20:先按出队顺序攒进第 2 层缓冲。
- 1220 的孩子 15 入队(孩子永远按左右顺序入队,方向只影响“怎么收”)。
- 1320 的孩子 7 入队(孩子永远按左右顺序入队,方向只影响“怎么收”)。 第 2 层(右→左):把出队序列 [9, 20] 翻转 → [20, 9]——队列里现在全是下一层,方向翻转,继续。
- 14出队 8:先按出队顺序攒进第 3 层缓冲。
- 158 的孩子 1 入队(孩子永远按左右顺序入队,方向只影响“怎么收”)。
- 168 的孩子 2 入队(孩子永远按左右顺序入队,方向只影响“怎么收”)。
- 17出队 10:先按出队顺序攒进第 3 层缓冲。
- 1810 的孩子 4 入队(孩子永远按左右顺序入队,方向只影响“怎么收”)。
- 1910 的孩子 6 入队(孩子永远按左右顺序入队,方向只影响“怎么收”)。
- 20出队 15:先按出队顺序攒进第 3 层缓冲。
- 2115 的孩子 5 入队(孩子永远按左右顺序入队,方向只影响“怎么收”)。
- 22出队 7:先按出队顺序攒进第 3 层缓冲。 第 3 层(左→右)收完:[8, 10, 15, 7]——队列里现在全是下一层,方向翻转,继续。
- 23出队 1:先按出队顺序攒进第 4 层缓冲。
- 24出队 2:先按出队顺序攒进第 4 层缓冲。
- 25出队 4:先按出队顺序攒进第 4 层缓冲。
- 26出队 6:先按出队顺序攒进第 4 层缓冲。
- 27出队 5:先按出队顺序攒进第 4 层缓冲。 第 4 层(右→左):把出队序列 [1, 2, 4, 6, 5] 翻转 → [5, 6, 4, 2, 1]——队列里现在全是下一层,方向翻转,继续。
⚠️ 容易写错的地方
✗ 错:改了孩子入队顺序
✓ 对:孩子永远先左后右入队
方向只决定“怎么收结果”,遍历顺序不能动,否则父子关系乱
✗ 错:忘切换 flag
✓ 对:每层结束 leftToRight = !leftToRight
不切换就退化成普通层序,不是锯齿
✗ 错:不锁本层个数
✓ 对:进 for 前先存 sz=q.size()
循环里队列在变长,分不出层也就翻不对
完整代码(Python / Java / C++)
Python
def zigzagLevelOrder(root):
if not root: return []
res, q, leftToRight = [], deque([root]), True
while q:
level = []
for _ in range(len(q)): # 锁定本层个数
node = q.popleft() # 出队一个
level.append(node.val) # 永远按出队(左→右)攒
if node.left: q.append(node.left) # 孩子照常左右入队
if node.right: q.append(node.right)
res.append(level if leftToRight else level[::-1]) # 偶数层翻转
leftToRight = not leftToRight # 方向交替
return resJava
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) return res;
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
boolean leftToRight = true;
while (!q.isEmpty()) {
int sz = q.size(); // 锁定本层个数
LinkedList<Integer> level = new LinkedList<>();
for (int i = 0; i < sz; i++) {
TreeNode node = q.poll(); // 出队一个
// 方向开关:左→右尾插,右→左头插,省去最后 reverse
if (leftToRight) level.addLast(node.val);
else level.addFirst(node.val);
if (node.left != null) q.offer(node.left); // 孩子照常左右入队
if (node.right != null) q.offer(node.right);
}
res.add(level);
leftToRight = !leftToRight; // 方向交替
}
return res;
}C++
vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
vector<vector<int>> res;
if (!root) return res;
queue<TreeNode*> q; q.push(root);
bool leftToRight = true;
while (!q.empty()) {
int sz = q.size(); // 锁定本层个数
vector<int> level(sz);
for (int i = 0; i < sz; ++i) {
TreeNode* node = q.front(); q.pop(); // 出队一个
// 方向开关:决定值写进本层数组的哪一端
int idx = leftToRight ? i : sz - 1 - i;
level[idx] = node->val;
if (node->left) q.push(node->left); // 孩子照常左右入队
if (node->right) q.push(node->right);
}
res.push_back(level);
leftToRight = !leftToRight; // 方向交替
}
return res;
}复杂度
时间
O(n)
每个节点入队出队各一次;偶数层翻转总计也是 O(n)
空间
O(n)
队列最多装下最宽一层的节点
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 二叉树的锯齿形层序遍历 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
锯齿层序和普通层序差在哪?+
遍历完全相同,都是队列 BFS;只是收集每层结果时,偶数层要反向(reverse 或用双端队列头插)。
能不能不额外翻转、在遍历时就排好?+
能。Java 用 LinkedList,左到右层 addLast、右到左层 addFirst;C++ 直接按 leftToRight 算落点下标 i 或 sz-1-i,免去最后的 reverse。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄把 二叉树的锯齿形层序遍历 一步步讲透(全站已上线 905 份讲透,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。