基于左孩子右兄弟表示法构建N叉树:文件键写入疑问
左孩子右兄弟N叉树的文件键值读写方案
嘿,先帮你指出结构体里的一个关键小bug——你的构造函数里把参数和成员变量的赋值写反了!应该是siblingsNumber = siblingsNr;而不是siblingsNr = siblingsNumber;,不然你的siblingsNumber根本没被正确初始化,后续操作肯定会出问题。先修正这个,咱们再聊文件读写的事。
1. 修正后的Node结构体
首先给你一个更健壮的结构体版本,加上析构函数防止内存泄漏,同时初始化siblings数组的元素为nullptr避免野指针:
#include <iostream> #include <fstream> struct Node { int data; int siblingsNumber; Node *LeftChild; Node **siblings; // 修正构造函数的赋值逻辑,初始化成员 Node(int key, int siblingsNr) : data(key), siblingsNumber(siblingsNr), LeftChild(nullptr) { siblings = new Node*[siblingsNr]; // 初始化所有兄弟指针为nullptr for (int i = 0; i < siblingsNr; ++i) { siblings[i] = nullptr; } } // 析构函数:递归释放所有子节点和兄弟节点的内存 ~Node() { delete LeftChild; // 释放左孩子 for (int i = 0; i < siblingsNumber; ++i) { delete siblings[i]; // 释放每个兄弟节点 } delete[] siblings; // 释放兄弟数组 } };
2. 将树的键值写入文件
左孩子右兄弟结构适合用先序遍历来写入文件,因为先访问父节点,再遍历左子树,最后依次遍历所有兄弟节点。写入时需要记录每个节点的data和siblingsNumber,这样读取时才能正确分配兄弟数组。
写入函数实现
// 递归写入树到文件 void writeTreeToFile(Node* root, std::ofstream& outFile) { if (!root) return; // 写入当前节点的data和兄弟数量,用空格分隔 outFile << root->data << " " << root->siblingsNumber << "\n"; // 先写入左子树 writeTreeToFile(root->LeftChild, outFile); // 依次写入每个兄弟节点 for (int i = 0; i < root->siblingsNumber; ++i) { writeTreeToFile(root->siblings[i], outFile); } } // 调用示例 int main() { // 构建一棵示例N叉树 Node* root = new Node(1, 0); // 根节点,无兄弟 root->LeftChild = new Node(2, 1); // 根的左孩子,有1个兄弟 root->LeftChild->siblings[0] = new Node(3, 0); // 节点2的兄弟节点3 root->LeftChild->LeftChild = new Node(4, 0); // 节点2的左孩子 // 打开文件准备写入 std::ofstream outFile("tree_keys.txt"); if (outFile.is_open()) { writeTreeToFile(root, outFile); outFile.close(); std::cout << "树的键值已成功写入文件!\n"; } else { std::cerr << "无法打开文件进行写入!\n"; } delete root; // 释放所有内存 return 0; }
执行后,tree_keys.txt的内容会是:
1 0 2 1 4 0 3 0
3. 从文件读取键值构建树
读取时同样用先序遍历的递归逻辑,按照写入的顺序依次读取每个节点的data和siblingsNumber,然后创建节点、递归构建左子树和兄弟节点。
读取函数实现
// 递归从文件读取并构建树 Node* readTreeFromFile(std::ifstream& inFile) { int data, siblingsNr; // 如果文件已读完,返回nullptr if (!(inFile >> data >> siblingsNr)) { return nullptr; } // 创建当前节点 Node* node = new Node(data, siblingsNr); // 先构建左子树 node->LeftChild = readTreeFromFile(inFile); // 依次构建每个兄弟节点 for (int i = 0; i < siblingsNr; ++i) { node->siblings[i] = readTreeFromFile(inFile); } return node; } // 调用示例 int main() { std::ifstream inFile("tree_keys.txt"); Node* root = nullptr; if (inFile.is_open()) { root = readTreeFromFile(inFile); inFile.close(); std::cout << "已从文件读取并构建树!\n"; } else { std::cerr << "无法打开文件进行读取!\n"; } // 验证:先序遍历打印树 auto printTree = [](auto&& self, Node* node) -> void { if (!node) return; std::cout << node->data << " "; self(self, node->LeftChild); for (int i = 0; i < node->siblingsNumber; ++i) { self(self, node->siblings[i]); } }; printTree(printTree, root); // 输出:1 2 4 3 delete root; // 释放内存 return 0; }
关键注意事项
- 构造函数的赋值错误:一定要修正
siblingsNumber = siblingsNr,否则兄弟数组的大小会是随机值,直接导致程序崩溃。 - 内存泄漏:必须添加析构函数,递归释放所有节点和数组内存,否则会造成内存泄漏。
- 文件格式一致性:写入和读取的格式必须严格对应,比如每个节点的两个值用空格分隔,每行一个节点,这样读取时才能正确解析。
- 空节点处理:如果你的树中有空的左孩子或兄弟节点,写入时不会记录这些空节点,读取时会自动跳过,不需要额外处理。
内容的提问来源于stack exchange,提问作者Mareș Ștefan
相关产品推荐
相关产品推荐

