从链表构建BST时程序崩溃,求问题排查帮助
咱们一步步拆解你代码里导致崩溃的核心问题,还有一些潜在的逻辑漏洞:
1. 全局root未被正确初始化(直接触发崩溃)
你的TransferToBST函数参数里的root是局部变量,函数内部对它的修改不会影响全局的root变量。初始全局root是NULL,执行TransferToBST(root,head)后,全局root依然是NULL,之后main里访问root->right就会触发空指针引用,直接导致程序崩溃。
修复方式:
让TransferToBST返回修改后的root,再赋值给全局变量:
BSTnode* TransferToBST(node* head) { BSTnode* root = NULL; while (head != NULL) { // 遍历所有节点,包括最后一个 root = BSTinsert(root, head); head = head->next; } return root; }
然后在main里更新调用逻辑:
root = TransferToBST(head);
2. TransferToBST循环条件错误,漏掉最后一个节点
原来的while (head->next!=NULL)只会处理到倒数第二个节点,最后一个节点会被跳过。改成while (head != NULL)才能遍历链表中所有节点。
3. BSTinsert未处理重复单词,且未利用head2存储文件路径
你的BSTnode里的head2设计是用来存储同一个单词的所有文件路径,但当前BSTinsert只会新建节点,不会判断单词是否已存在。如果遇到重复单词,应该把文件路径添加到head2链表中,而不是新建BST节点,否则会导致BST结构混乱。
修复后的BSTinsert:
BSTnode* BSTinsert(BSTnode* root, node* data) { if(root == NULL) { BSTnode* new_node = (BSTnode*)malloc(sizeof(BSTnode)); strcpy(new_node->word, data->word); new_node->left = NULL; new_node->right = NULL; // 初始化head2链表,存入当前文件路径 new_node->head2 = (node*)malloc(sizeof(node)); strcpy(new_node->head2->FileName, data->FileName); new_node->head2->next = NULL; return new_node; } int cmp_result = strcmp(data->word, root->word); if (cmp_result > 0) { root->right = BSTinsert(root->right, data); } else if (cmp_result < 0) { root->left = BSTinsert(root->left, data); } else { // 单词已存在,将文件路径追加到head2链表 node* new_file_node = (node*)malloc(sizeof(node)); strcpy(new_file_node->FileName, data->FileName); new_file_node->next = root->head2; root->head2 = new_file_node; } return root; }
4. insert函数存在内存泄漏
原insert函数中额外malloc的temp只是用来遍历链表,最后未释放,造成内存泄漏。同时初始化头节点时无需额外malloc,直接复用已创建的节点即可:
void insert(char string[], char* file1) { node* a = (node*)malloc(sizeof(node)); strcpy(a->word, string); strcpy(a->FileName, file1); a->next = NULL; if (head == NULL) { head = a; // 直接用a作为头节点,避免多余malloc } else { node* temp = head; while (temp->next != NULL) { temp = temp->next; } temp->next = a; } }
5. linebyline中feof使用错误
feof(ptr)只有在读取到文件末尾后才会返回真,原逻辑会导致fgets读取失败后仍执行i++,产生无效的空行。同时别忘了检查文件是否打开成功,以及读取完成后关闭文件:
void linebyline(char* file1) { int i = 0; char sample[1000][1000]; FILE* ptr = fopen(file1, "r"); if (ptr == NULL) { // 检查文件是否打开成功 printf("Failed to open file: %s\n", file1); return; } // 用fgets的返回值判断读取是否成功 while (fgets(sample[i], 1000, ptr) != NULL) { i++; } for (int j = 0; j < i; j++) { word(sample[j], file1); } fclose(ptr); // 关闭文件,避免资源泄漏 }
6. word函数的逻辑漏洞
原函数未处理字符串末尾的单词,且没有统一单词大小写(会导致Hello和hello被视为不同单词),优化后的版本:
void word(char s1[], char* file1) { char a[100]; int i, j = 0; int length = strlen(s1); for (i = 0; i < length; i++) { if (!((s1[i] >= 'a' && s1[i] <= 'z') || (s1[i] >= 'A' && s1[i] <= 'Z'))) { if (j > 0) { // 仅当a中有有效字符时才插入 a[j] = '\0'; // 统一转小写,消除大小写差异 for (int k = 0; a[k] != '\0'; k++) { if (a[k] >= 'A' && a[k] <= 'Z') { a[k] += 32; } } insert(a, file1); j = 0; } } else { a[j] = s1[i]; j++; if (j >= 99) { // 防止数组越界 a[j] = '\0'; insert(a, file1); j = 0; } } } // 处理字符串末尾的剩余单词 if (j > 0) { a[j] = '\0'; for (int k = 0; a[k] != '\0'; k++) { if (a[k] >= 'A' && a[k] <= 'Z') { a[k] += 32; } } insert(a, file1); } }
最后修复后的main函数
int main() { readAllFiles("m"); root = TransferToBST(head); // 先检查root是否非空,避免空指针访问 if (root != NULL) { // 示例:遍历右子树测试 BSTnode* temp = root; while (temp != NULL) { printf("%s\n", temp->word); temp = temp->right; } } else { printf("BST is empty!\n"); } // 可选:添加内存释放逻辑,避免长期运行内存泄漏 // freeLinkedList(head); // freeBST(root); return 0; }
把这些问题逐一修复后,程序应该就能正常运行且逻辑更完整了。
内容的提问来源于stack exchange,提问作者Mahdi Ghajary

