题目

给你二叉树的根结点 root ,请你设计算法计算二叉树的 垂序遍历 序列。

对位于 (row, col) 的每个结点而言,其左右子结点分别位于 (row + 1, col - 1) 和 (row + 1, col + 1) 。树的根结点位于 (0, 0) 。

二叉树的 垂序遍历 从最左边的列开始直到最右边的列结束,按列索引每一列上的所有结点,形成一个按出现位置从上到下排序的有序列表。如果同行同列上有多个结点,则按结点的值从小到大进行排序。

返回二叉树的 垂序遍历 序列。

示例 1:

img

输入:root = [3,9,20,null,null,15,7]
输出:[[9],[3,15],[20],[7]]
解释:
列 -1 :只有结点 9 在此列中。
列  0 :只有结点 3 和 15 在此列中,按从上到下顺序。
列  1 :只有结点 20 在此列中。
列  2 :只有结点 7 在此列中。

示例 2:

img

输入:root = [1,2,3,4,5,6,7]
输出:[[4],[2],[1,5,6],[3],[7]]
解释:
列 -2 :只有结点 4 在此列中。
列 -1 :只有结点 2 在此列中。
列  0 :结点 1 、5 和 6 都在此列中。
          1 在上面,所以它出现在前面。
          5 和 6 位置都是 (2, 0) ,所以按值从小到大排序,5 在 6 的前面。
列  1 :只有结点 3 在此列中。
列  2 :只有结点 7 在此列中。

示例 3:

img

输入:root = [1,2,3,4,6,5,7]
输出:[[4],[2],[1,5,6],[3],[7]]
解释:
这个示例实际上与示例 2 完全相同,只是结点 5 和 6 在树中的位置发生了交换。
因为 5 和 6 的位置仍然相同,所以答案保持不变,仍然按值从小到大排序。

提示:

  • 树中结点数目总数在范围 [1, 1000] 内
  • 0 <= Node.val <= 1000

解答

解题思路如下:

  1. 编写了一个辅助函数 verticalTraversalHelper,用于递归处理二叉树。该函数的参数包括当前结点 root、当前结点所在的列数 x、当前结点所在的行数 y,以及一个用于存储结点值的 map 结构 nodes。在该函数中,首先判断当前结点是否为空,若为空则直接返回;否则,将当前结点的值添加到 nodes 结构中对应的列和行中,并递归调用该函数处理左右子树,列数 x 根据左右移动。
  2. 定义了主函数 verticalTraversal,用于实现按照列的顺序遍历二叉树。在该函数中,首先定义了一个二维数组 result,用于存储最终结果。然后判断根结点是否为空,若为空则直接返回空数组。接下来使用 map 结构 nodes 来存储每一列的结点值,其中 key 为列数,value 为行数和结点值的映射。
  3. 调用辅助函数 verticalTraversalHelper 处理二叉树,将结点按照列的顺序分组,存储在 nodes 结构中。
  4. 使用队列 columns 对列数进行排序,然后按照列数从小到大遍历结点值,并将其添加到结果数组 result 中。在遍历过程中,首先从队列中取出一个列数 col,然后使用优先队列 pq 对该列的行数和结点值进行排序。将当前列的结点值按照行数加入优先队列。
  5. 将优先队列中的结点值按照行数顺序取出,存储在一个临时数组 colValues 中。
  6. 将 colValues 数组添加到结果数组 result 中。
  7. 最后返回结果数组 result。
  8. 在 main 函数中,创建了一个测试二叉树,并调用 verticalTraversal 函数进行遍历,然后打印结果。
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    // 辅助函数,用于将二叉树的结点按照列的顺序进行分组
    void verticalTraversalHelper(TreeNode *root, int x, int y, map<int, map<int, vector<int>>> &nodes) {
        if (!root)
            return;

        // 在对应的列和行中添加当前结点的值
        nodes[x][y].push_back(root->val);

        // 递归处理左子树和右子树,列数x根据左右移动
        verticalTraversalHelper(root->left, x - 1, y + 1, nodes);
        verticalTraversalHelper(root->right, x + 1, y + 1, nodes);
    }
    // 主函数,返回按照列的顺序遍历的结点值的二维数组
    vector<vector<int>> verticalTraversal(TreeNode *root) {
        // 存储结点值的二维数组
        vector<vector<int>> result;

        // 如果根结点为空,直接返回空数组
        if (!root)
            return result;

        // 使用map来存储每一列的结点值,key为列数,value为行数和结点值的映射
        map<int, map<int, vector<int>>> nodes;

        // 递归处理二叉树,将结点按照列的顺序分组
        verticalTraversalHelper(root, 0, 0, nodes);

        // 使用队列对列数进行排序
        queue<int> columns;
        for (auto &node : nodes)
            columns.push(node.first);

        // 按照列数从小到大遍历结点值,并添加到结果数组中
        while (!columns.empty()) {
            int col = columns.front();
            columns.pop();

            // 使用优先队列对行数和结点值进行排序
            priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;

            // 将当前列的结点值按照行数加入优先队列
            for (auto &node : nodes[col]) {
                int row = node.first;
                for (int val : node.second) {
                    pq.push({row, val});
                }
            }

            // 将当前列的结点值按照行数顺序添加到结果数组中
            vector<int> colValues;
            while (!pq.empty()) {
                colValues.push_back(pq.top().second);
                pq.pop();
            }

            result.push_back(colValues);
        }

        return result;
    }

};