基于二叉树的国家猜谜游戏文件加载与二叉树重建问题求助
国家猜谜游戏二叉树持久化问题解决方案
问题根源
当前代码的核心问题是没有在文件中区分叶子节点(国家答案)和非叶子节点(问题):保存时所有节点都直接写入文本,加载时不管什么节点都会递归创建yes和no子节点,最终只有最后一个节点会因为读到文件末尾而没有子节点,被当作答案,其他节点都被错误地视为带子节点的问题节点。你尝试的Q:/A:前缀思路是对的,但之前没在加载逻辑里利用这个标记判断节点类型,所以没生效。
解决方案
我们需要修改保存和加载逻辑,用前缀明确标记节点类型:
- 保存时:给非叶子节点(问题)加
Q:前缀,叶子节点(国家答案)加A:前缀 - 加载时:根据前缀判断节点类型,
A:开头的是叶子节点,不需要创建子节点;Q:开头的是问题节点,递归加载yes和no子节点
修改后的完整代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_LENGTH 256 typedef struct TreeNode { char content[MAX_LENGTH]; // 重命名为content,同时存储问题或答案 struct TreeNode* yes; struct TreeNode* no; } TreeNode; TreeNode* createNode(const char* content) { TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode)); strncpy(newNode->content, content, MAX_LENGTH - 1); newNode->content[MAX_LENGTH - 1] = '\0'; newNode->yes = NULL; newNode->no = NULL; return newNode; } void saveTreeToFile(TreeNode* node, FILE* file) { if (node == NULL) { return; } if (node->yes == NULL && node->no == NULL) { // 叶子节点(国家答案),加A:前缀 fprintf(file, "A: %s\n", node->content); } else { // 非叶子节点(问题),加Q:前缀 fprintf(file, "Q: %s\n", node->content); saveTreeToFile(node->yes, file); saveTreeToFile(node->no, file); } } TreeNode* loadTreeFromFile(FILE* file) { char line[MAX_LENGTH]; if (fgets(line, MAX_LENGTH, file) == NULL) { return NULL; } // 去掉换行符 size_t len = strlen(line); if (len > 0 && line[len - 1] == '\n') { line[len - 1] = '\0'; } // 根据前缀判断节点类型 if (strncmp(line, "A: ", 3) == 0) { // 叶子节点,提取答案内容,无需创建子节点 TreeNode* newNode = createNode(line + 3); return newNode; } else if (strncmp(line, "Q: ", 3) == 0) { // 问题节点,提取问题内容,递归加载子节点 TreeNode* newNode = createNode(line + 3); newNode->yes = loadTreeFromFile(file); newNode->no = loadTreeFromFile(file); return newNode; } // 无效行,返回NULL(理论上不会出现) return NULL; } void playGame(TreeNode* node) { char answer; // 叶子节点:猜国家 if (node->yes == NULL && node->no == NULL) { printf("Is it %s? (y/n): ", node->content); scanf(" %c", &answer); if (answer == 'y') { printf("I guessed correctly!\n"); } else { char userCountry[MAX_LENGTH]; printf("I give up. What was the country you were thinking of? "); scanf(" %[^\n]", userCountry); char newQuestion[MAX_LENGTH]; printf("What question should I have asked to distinguish %s from %s? ", node->content, userCountry); scanf(" %[^\n]", newQuestion); // 更新树结构 node->yes = createNode(userCountry); node->no = createNode(node->content); strncpy(node->content, newQuestion, MAX_LENGTH - 1); node->content[MAX_LENGTH - 1] = '\0'; } } else { // 非叶子节点:问问题 printf("%s (y/n): ", node->content); scanf(" %c", &answer); if (answer == 'y' && node->yes != NULL) { playGame(node->yes); } else if (answer == 'n' && node->no != NULL) { playGame(node->no); } else { printf("Invalid input. Please enter 'y' or 'n'.\n"); } } } int main() { TreeNode* root; FILE* file = fopen("tree.txt", "r"); if (file == NULL) { // 初始树:问题+两个国家答案 root = createNode("Is it a European country?"); root->yes = createNode("France"); root->no = createNode("Germany"); } else { root = loadTreeFromFile(file); fclose(file); } char playAgain; do { playGame(root); printf("Would you like to play again? (y/n): "); scanf(" %c", &playAgain); } while (playAgain == 'y'); // 保存树到文件 file = fopen("tree.txt", "w"); if (file != NULL) { saveTreeToFile(root, file); fclose(file); } else { fprintf(stderr, "Error opening file for writing.\n"); } return 0; }
关键修改说明
- 字段重命名:把
question改为content,因为它既存问题也存答案,更准确 - 保存逻辑:
- 叶子节点(无子女)写入
A: 国家名称 - 非叶子节点(有子女)写入
Q: 问题内容,再递归保存子节点
- 叶子节点(无子女)写入
- 加载逻辑:
- 识别
A:前缀,创建无子女的叶子节点 - 识别
Q:前缀,创建问题节点并递归加载子节点
- 识别
- 游戏逻辑:仅将原来的
question替换为content,其他逻辑保持不变
修改后,文件内容会清晰区分问题和答案,加载时能准确还原二叉树结构,重启游戏后也能正常读取之前的学习内容。
内容的提问来源于stack exchange,提问作者Youssef El Garawany
相关产品推荐
相关产品推荐

