C语言链表按值出现次数阈值删除节点的实现及段错误修复
触发段错误的核心问题
- 标识符拼写偏差:代码中多处使用未定义的变量、函数名,比如
deleteOccurrences内误用不存在的Lista、testa标识符,countOccurrences循环内错写head = testa->next,调用统计函数时将countOccurrences少写一个字母变成countOccurences,这类错误会直接造成非法内存访问。 - 指针传递失效:
manageList目前直接接收头指针的值,内部调用删除函数时取的是函数局部指针的地址,修改结果完全无法传回main函数;如果链表头节点本身需要被删除,main内的头指针依然会指向已经被释放的内存块,触发野指针访问。 - 遍历逻辑漏洞:原
manageList的循环中,删除操作完成后直接执行head = head->next,如果当前节点就是被删除的目标,就会访问已经被释放的节点内存;同时重复遍历统计的逻辑冗余,还会因为前面的删除操作破坏链表结构,导致后续遍历越界。
修正实现方案
调整后的执行逻辑完全匹配需求,从根源上避免上述问题:
- 逐节点校验值的出现次数,达到阈值就删除所有该值的节点
- 所有涉及链表头修改的操作,传入头指针的地址,保证修改结果同步到调用方
- 每完成一次删除操作,重置遍历起点到链表头,避免因为链表结构变化导致的遍历错位
可运行的完整修正代码
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> // 节点定义 typedef struct El{ int val; struct El *next; }ElemList; typedef ElemList *List; // 头插法实现,与原有逻辑一致 List insert(List head, int val) { ElemList *new_node = (ElemList*)malloc(sizeof(ElemList)); new_node->val = val; new_node->next = head; return new_node; } // 统计指定值在链表中的出现次数 int countOccurrences(List head, int val) { int count = 0; while(head != NULL) { if(head->val == val) { count++; } head = head->next; } return count; } // 删除链表中所有值为val的节点,传入头指针地址保证头节点可修改 void deleteOccurrences(List *head, int val) { ElemList *temp = *head, *prev = NULL; // 处理连续为目标值的头节点 while(temp != NULL && temp->val == val) { *head = temp->next; free(temp); temp = *head; } // 处理链表中间的目标值节点 while(temp != NULL) { while(temp != NULL && temp->val != val) { prev = temp; temp = temp->next; } if(temp == NULL) return; prev->next = temp->next; free(temp); temp = prev->next; } } // 链表管理逻辑,支持可配置的删除阈值 void manageList(List *head, int threshold) { ElemList *cur = *head; while(cur != NULL) { int current_val = cur->val; int occur_count = countOccurrences(*head, current_val); // 达到阈值就删除所有该值的节点 if(occur_count >= threshold) { deleteOccurrences(head, current_val); // 删除完成后重置遍历起点到链表头,避免错位 cur = *head; } else { cur = cur->next; } } } // 打印链表工具函数 void printList(List head) { while(head != NULL) { printf("%d", head->val); if(head->next != NULL) printf("->"); head = head->next; } printf("\n"); } int main() { List head = NULL; // 头插法构建目标链表,最终顺序为1->3->8->5->6->8->3->8->9 head = insert(head,9); head = insert(head,8); head = insert(head,3); head = insert(head,8); head = insert(head,6); head = insert(head,5); head = insert(head,8); head = insert(head,3); head = insert(head,1); printf("原链表:"); printList(head); manageList(&head, 3); printf("阈值≥3删除后链表:"); printList(head); return 0; }
运行效果验证
按照示例场景,值为8的节点共出现3次,达到删除阈值会被全部移除,最终输出的链表为1->3->5->6->3->9,与需求预期完全一致。
性能优化提示:如果链表存储的数值范围较大,可以把逐次调用计数函数的逻辑替换为首轮全链表遍历,用哈希表一次性统计所有值的出现次数,减少重复遍历的性能开销。
内容的提问来源于stack exchange,提问作者Marco
相关产品推荐
相关产品推荐

