You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C++中打印可视化AVL树结构,适配任意长度节点数值?

实现思路
  • 第一步:提前遍历所有节点,将节点值转为字符串并统计最大字符串长度,所有节点统一按该长度居中占位,保证不同长度的节点值不会打乱排版
  • 第二步:基于AVL树自带的高度属性,按层序遍历(BFS)逐层输出节点,每层的缩进和节点间距根据树高和最大节点宽度动态计算
  • 第三步:每输出一层节点后,额外输出一层连接线(/和\),连接线的位置根据下一层子节点的位置动态对齐
完整实现代码

首先在你的AVL树类中添加如下成员函数,注意需要提前引入<sstream>、<queue>、<iomanip>、<vector>、<string>头文件:

template<typename T>
class AVLTree {
private:
    AVLTreeNode<T>* root;

    // 辅助函数:将节点值转为字符串
    std::string nodeToString(const T& val) const {
        std::ostringstream oss;
        oss << val;
        return oss.str();
    }

    // 辅助函数:统计所有节点的最大字符串宽度
    int getMaxNodeWidth(AVLTreeNode<T>* node) const {
        if (!node) return 0;
        int curr_len = nodeToString(node->data).size();
        int left_len = getMaxNodeWidth(node->LChild);
        int right_len = getMaxNodeWidth(node->RChild);
        return std::max({curr_len, left_len, right_len});
    }

public:
    // 原有构造、插入、删除等接口省略

    // 打印AVL树的主函数
    void printTree() const {
        if (!root) {
            std::cout << "空树" << std::endl;
            return;
        }

        int max_width = getMaxNodeWidth(root);
        int tree_height = root->height;
        // 每个节点的占位宽度,最小为1
        int node_width = std::max(max_width, 1);

        // 层序遍历队列,元素为节点指针和当前层数
        std::queue<std::pair<AVLTreeNode<T>*, int>> q;
        q.push({root, 0});

        int current_level = 0;
        // 存储当前层的所有节点字符串
        std::vector<std::string> curr_level_nodes;

        while (!q.empty()) {
            // 低版本C++可替换为 pair 的 first/second 访问
            auto [node, level] = q.front();
            q.pop();

            // 进入新的一层,先打印上一层的内容
            if (level != current_level) {
                // 打印上一层节点
                int indent = (1 << (tree_height - current_level)) * (node_width + 1) / 2 - node_width / 2;
                int gap = (1 << (tree_height - current_level)) * (node_width + 1) - node_width;
                std::cout << std::string(indent, ' ');
                for (size_t i = 0; i < curr_level_nodes.size(); ++i) {
                    std::cout << std::setw(node_width) << std::left << curr_level_nodes[i];
                    if (i != curr_level_nodes.size() - 1) {
                        std::cout << std::string(gap, ' ');
                    }
                }
                std::cout << std::endl;

                // 打印上一层和当前层之间的连接线
                if (current_level < tree_height - 1) {
                    int line_indent = (1 << (tree_height - current_level - 1)) * (node_width + 1) / 2;
                    int line_gap = (1 << (tree_height - current_level)) * (node_width + 1);
                    std::cout << std::string(line_indent, ' ');
                    for (size_t i = 0; i < curr_level_nodes.size(); ++i) {
                        bool has_left = (i*2 < curr_level_nodes.size() && !curr_level_nodes[i*2].empty());
                        bool has_right = (i*2+1 < curr_level_nodes.size() && !curr_level_nodes[i*2+1].empty());
                        if (has_left) std::cout << "/";
                        else std::cout << " ";
                        std::cout << std::string((1 << (tree_height - current_level -1)) * (node_width +1) -1, ' ');
                        if (has_right) std::cout << "\\";
                        else std::cout << " ";
                        if (i != curr_level_nodes.size() -1) {
                            std::cout << std::string(line_gap - 2, ' ');
                        }
                    }
                    std::cout << std::endl;
                }

                curr_level_nodes.clear();
                current_level = level;
            }

            // 存储当前节点的字符串,空节点存空串
            if (node) {
                curr_level_nodes.push_back(nodeToString(node->data));
                q.push({node->LChild, level + 1});
                q.push({node->RChild, level + 1});
            } else {
                curr_level_nodes.push_back("");
                // 空节点也需要加空的子节点占位,保证层的大小一致
                q.push({nullptr, level + 1});
                q.push({nullptr, level + 1});
            }

            // 所有层都处理完了,打印最后一层
            if (q.empty()) {
                int indent = (1 << (tree_height - current_level)) * (node_width + 1) / 2 - node_width / 2;
                int gap = (1 << (tree_height - current_level)) * (node_width + 1) - node_width;
                std::cout << std::string(indent, ' ');
                for (size_t i = 0; i < curr_level_nodes.size(); ++i) {
                    std::cout << std::setw(node_width) << std::left << curr_level_nodes[i];
                    if (i != curr_level_nodes.size() - 1) {
                        std::cout << std::string(gap, ' ');
                    }
                }
                std::cout << std::endl;
            }

            // 提前终止:当前层已经是最后一层,不需要再往下处理空节点
            if (level >= tree_height) break;
        }
    }
};

注意:如果你的编译环境不支持C++17及以上标准,把代码中的auto [node, level] = q.front();替换为如下写法即可:

AVLTreeNode<T>* node = q.front().first;
int level = q.front().second;
效果验证

插入1、2、3三个节点后调用printTree(),输出效果如下:

2 
 / \
1   3

如果插入节点值为12、3、1234,最大节点宽度为4,输出会自动适配:

12   
   /  \  
 3    1234

内容的提问来源于stack exchange,提问作者Shuliang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 03:24:04