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

基于二叉树的国家猜谜游戏文件加载与二叉树重建问题求助

国家猜谜游戏二叉树持久化问题解决方案

问题根源

当前代码的核心问题是没有在文件中区分叶子节点(国家答案)和非叶子节点(问题):保存时所有节点都直接写入文本,加载时不管什么节点都会递归创建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;
}

关键修改说明

  1. 字段重命名:把question改为content,因为它既存问题也存答案,更准确
  2. 保存逻辑:
    • 叶子节点(无子女)写入A: 国家名称
    • 非叶子节点(有子女)写入Q: 问题内容,再递归保存子节点
  3. 加载逻辑:
    • 识别A:前缀,创建无子女的叶子节点
    • 识别Q:前缀,创建问题节点并递归加载子节点
  4. 游戏逻辑:仅将原来的question替换为content,其他逻辑保持不变

修改后,文件内容会清晰区分问题和答案,加载时能准确还原二叉树结构,重启游戏后也能正常读取之前的学习内容。

内容的提问来源于stack exchange,提问作者Youssef El Garawany

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 10:09:50