如何在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
相关产品推荐
相关产品推荐

