垂序遍历是列优先、行有序的投影遍历:根列号为0,左减1右加1;同一列节点按行升序,行相同时按值升序。需用map存列号(自动升序),每列vector存(row,val),遍历后按row、再val排序。

垂序遍历的核心是列优先 + 行有序
垂序遍历不是按层或按深度,而是把二叉树“投影”到垂直线上:根节点列号为 0,左子节点列号减 1,右子节点列号加 1。同一列的节点按行号(从上到下)升序排列;若行号相同(即同一层的左右子节点),按节点值升序(题目通常要求稳定排序,实际需按原访问顺序,但标准 OJ 如 LeetCode 987 明确要求值升序)。关键在于不能只存列号,必须同时记录 row 和 col,否则无法区分同列不同行的顺序。
用 map<int vector int treenode>>></int> 存列与节点三元组
直接用 map 按列号自动排序,每个列对应一个 vector,里面存 (row, val, node) 或 (row, val)(val 可现场取,不一定要存指针)。注意:map 的 key 是 int 列号,天然升序;内部 vector 需在遍历完后手动按 row 升序、再按 val 升序排序。BFS 和 DFS 都可以,但 BFS 天然按行序访问,row 值更直观可控。
- BFS 时每入队一个节点,记录其
row和col(根为 (0,0),左子为 (row+1, col-1),右子为 (row+1, col+1)) - DFS 递归时传入当前
row和col,每次调用更新 - 避免用
unordered_map:它不保证列号顺序,最后还得额外收集 key 并排序,反而更麻烦
排序时别漏掉 row 相同情况下的 val 比较
LeetCode 987 明确要求:“如果两个节点位置相同,则较小值的节点会先出现”。这意味着对同一列的节点,在按 row 排完后,必须对 row 相同的子组再按 val 排序。常见错误是只排了 row,导致 [1,2,3] 同列同层时输出顺序错乱。C++ 中可用 sort(vec.begin(), vec.end(), [](const auto& a, const auto& b) { return get(a) != get(b) ? get(a) (b) : get(a) (b); })。
-
get(a)是row,get(a)是val - 不要用
pair<int></int>存 (row,val),因为默认比较是先比 first 再比 second,刚好符合要求,更简洁 - 若用
vector<array>></array>,注意array的字典序比较也等价于 pair
返回结果前记得把 map 的 value 转成二维 vector<vector>></vector>
map 迭代器天然按列号从小到大遍历,所以直接 for 循环即可。每个 value 是已排序的节点值列表,直接 push_back 到结果中。容易忽略的是:空树要返回空 vector;单节点树只有一列一个元素;极端左倾树会产生负列号,map 照样支持,无需偏移处理。
立即学习“C++免费学习笔记(深入)”;
- 不要手写列号范围扫描(比如从 -1000 到 1000),那是哈希表思维,不是
map的用法 - 别在循环里反复调用
map::size()或map::begin(),效率无影响但语义不清 - 如果用 DFS,注意递归栈深度——题目一般给定节点数 ≤ 1000,安全;BFS 更稳
val 比较分支,列号映射逻辑倒很少出错。


















