垂直列序遍历的核心是显式维护列号:根节点列号为0,左子减1、右子加1;必须用BFS按(row, col)记录并分组,再对每列内节点按row升序排序,最终按列号升序输出二维列表。

垂直列序遍历的核心是列号而非层高
直接按层序或深度优先遍历无法天然得到“同一列”的节点,必须显式维护每个节点的列偏移。根节点列号设为 0,左子节点列号为 col - 1,右子节点为 col + 1。关键不是“从上到下”,而是“从左到右按列分组”,且同一列内要求**按行号升序、行内按从左到右顺序(即 BFS 顺序)**——这意味着不能只靠 DFS 记录列号,必须同时记录行号,并在最后对每列内节点按 (row, col) 排序。
用 map<int vector int>>></int> 存列与节点
列号可能为负,map 自动排序键值,正好满足“从最左列到最右列”输出要求。每个列对应一个 vector,存 (row, value) 对,后续按 row 升序排序即可。BFS 实现最稳妥:用 queue 存 (node, row, col),避免 DFS 的栈深度和顺序不确定性。
常见错误:用 unordered_map 存列 → 输出列序乱;只存 value 不存 row → 同一列内上下顺序错;DFS 中先递归左再右,但若某列有多个深度节点,仍无法保证 BFS 意义下的从左到右。
- 初始化队列:
queue<tuple int>></tuple>或自定义结构体 - 根节点入队:
(root, 0, 0) - 每次出队后,将
(row, node->val)压入cols[col] - 遍历完后,对每个
cols[col]调用sort,按row主序、必要时加index辅助(但 BFS 天然保左序,仅按row排即可)
避免 C++ 中 map 迭代器失效和空指针访问
map 在遍历时插入不会导致迭代器失效,但要注意:如果树为空,root == nullptr,需提前返回空结果;BFS 中每次取 front() 后必须 pop(),否则死循环;访问 node->left 或 node->right 前必须判空。
立即学习“C++免费学习笔记(深入)”;
性能影响:map 插入复杂度 O(log n),总时间约 O(n log n)(n 为节点数),空间 O(n)。若追求极致性能,可用 unordered_map + 额外数组存列范围,但代码变复杂,一般场景不必要。
- 判空写法必须为:
if (!node) continue;或if (node->left) q.push(...) - 不要用
cols[col].push_back({row, node->val})前不检查node - 迭代
map时直接用for (auto& [col, vec] : cols)(C++17),无需手动begin()/end()
输出格式要严格匹配题目要求
LeetCode 314 题要求返回 vector<vector>></vector>,每内层 vector 是一列从上到下的值。注意:不是按“层”输出,而是按“列”输出;同一列内必须是上→下顺序,不能颠倒。
容易被忽略的点:当两节点行号相同、列号相同时(理论上不会,因每个节点位置唯一),实际无需处理;但若用 DFS 且未记录行号,仅靠递归深度,遇到倾斜树时深度相同但实际行不同(比如右斜树中,某些节点虽同深度但物理位置不在同一水平线),就会出错。BFS 的 row 是真实层级计数,更可靠。
示例:输入 [3,9,20,null,null,15,7],输出应为 [[9],[3,15],[20],[7]] —— 列号分别为 -1,0,1,2,其中列 0 包含根 3 和其右子的左子 15(row=2),顺序必须是 3 在前、15 在后。


















