链表交集函数实现异常:返回完整首链表而非仅公共元素
链表交集函数错误排查与修复
问题描述
需要实现一个计算两个链表交集的函数,返回包含公共元素的新链表。当前实现的Intersect函数未正确工作:测试用例中,链表i1元素为{0,1,2},链表i2元素为{4,0,2},期望返回交集{0,2},但实际返回了整个i1的元素。
相关代码文件
elemtype.c
#define _CRT_SECURE_NO_WARNINGS #include "elemtype.h" #include <string.h> #include <stdlib.h> #define _unused(x) ((void)(x)) int ElemCompare(const ElemType *e1, const ElemType *e2) { return (*e1 > *e2) - (*e1 < *e2); } ElemType ElemCopy(const ElemType *e) { return *e; } void ElemSwap(ElemType *e1, ElemType *e2) { ElemType tmp = *e1; *e1 = *e2; *e2 = tmp; } void ElemDelete(ElemType *e) { _unused(e); } int ElemRead(FILE *f, ElemType *e) { return fscanf(f, "%d", e); } int ElemReadStdin(ElemType *e) { return ElemRead(stdin, e); } void ElemWrite(const ElemType *e, FILE *f) { fprintf(f, "%d", *e); } void ElemWriteStdout(const ElemType *e) { ElemWrite(e, stdout); }
elemtype.h
#ifndef ELEMTYPE_INT_H_ #define ELEMTYPE_INT_H_ #include <stdbool.h> #include <stdio.h> typedef int ElemType; int ElemCompare(const ElemType *e1, const ElemType *e2); ElemType ElemCopy(const ElemType *e); void ElemSwap(ElemType *e1, ElemType *e2); void ElemDelete(ElemType *e); int ElemRead(FILE *f, ElemType *e); int ElemReadStdin(ElemType *e); void ElemWrite(const ElemType *e, FILE *f); void ElemWriteStdout(const ElemType *e); #endif // ELEMTYPE_INT_H_
list.c
#define _CRT_SECURE_NO_WARNINGS #include "list.h" #include <string.h> #include <stdlib.h> /*****************************************************************************/ /* Item & Primitives */ /*****************************************************************************/ Item *ListCreateEmpty(void) { return NULL; } Item *ListInsertHead(const ElemType *e, Item *i) { Item *n = malloc(sizeof(Item)); n->value = ElemCopy(e); n->next = i; return n; } bool ListIsEmpty(const Item *i) { return i == NULL; } const ElemType *ListGetHeadValue(const Item *i) { if (ListIsEmpty(i)) { printf("ERROR: null list\n"); exit(1); } else { return &i->value; } } Item *ListGetTail(const Item *i) { if (ListIsEmpty(i)) { printf("ERROR: null list \n"); exit(2); } else { return i->next; } } Item *ListInsertBack(Item *i, const ElemType *e) { Item *n = ListInsertHead(e, ListCreateEmpty()); if (ListIsEmpty(i)) { return n; } Item *tmp = i; while (!ListIsEmpty(ListGetTail(tmp))) { tmp = ListGetTail(tmp); } tmp->next = n; return i; } void ListDelete(Item *i) { while (!ListIsEmpty(i)) { Item *tmp = i; i = i->next; ElemDelete(&tmp->value); free(tmp); } } /*****************************************************************************/ /* Non Primitives */ /*****************************************************************************/ void ListWrite(const Item *i, FILE *f) { fprintf(f, "["); while (!ListIsEmpty(i)) { ElemWrite(&i->value, f); i = ListGetTail(i); if (!ListIsEmpty(i)) { fprintf(f, ", "); } } fprintf(f, "]\n"); } void ListWriteStdout(const Item *i) { ListWrite(i, stdout); }
list.h
#ifndef LIST_H_ #define LIST_H_ #include "elemtype.h" #include <stdbool.h> #include <stdio.h> /*****************************************************************************/ /* Item & Primitives */ /*****************************************************************************/ struct Item { ElemType value; struct Item *next; }; typedef struct Item Item; Item *ListCreateEmpty(void); Item *ListInsertHead(const ElemType *e, Item *i); bool ListIsEmpty(const Item *i); const ElemType *ListGetHeadValue(const Item *i); Item *ListGetTail(const Item *i); Item *ListInsertBack(Item *i, const ElemType *e); void ListDelete(Item *i); /*****************************************************************************/ /* Non Primitives */ /*****************************************************************************/ void ListWrite(const Item *i, FILE *f); void ListWriteStdout(const Item *i); #endif // LIST_H_
错误的intersect.c实现
#include "elemtype.h" #include "list.h" Item* Intersect(const Item* i1, const Item* i2) { Item* ris = ListCreateEmpty(); for (; !ListIsEmpty(i1); i1 = ListGetTail(i1)) { for (const Item* tmp2 = i2; !ListIsEmpty(tmp2); tmp2 = ListGetTail(i2)) { if (ElemCompare(ListGetHeadValue(i1), ListGetHeadValue(tmp2))) { ris = ListInsertBack(ris, ListGetHeadValue(i1)); break; } } } return ris; } int main(void) { ElemType v1[] = { 0, 1, 2}; Item* i1 = ListCreateEmpty(); for (size_t i = 0; i < 3; i++) { i1 = ListInsertHead(v1 + i, i1); } ElemType v2[] = { 4, 0, 2 }; Item* i2 = ListCreateEmpty(); for (size_t i = 0; i < 3; i++) { i2 = ListInsertHead(v2 + i, i2); } Item* i3 = Intersect(i1, i2); ListDelete(i3); ListDelete(i1); ListDelete(i2); return 0; }
错误原因分析
- 内层循环遍历错误:内层循环中,
tmp2 = ListGetTail(i2)应该改为tmp2 = ListGetTail(tmp2)。原代码每次循环都从i2的尾部开始,导致内层循环无法遍历整个i2链表,最终逻辑错误。 - 比较条件反向:
ElemCompare函数返回0表示两个元素相等,原代码中if (ElemCompare(...))会在元素不相等时执行插入操作,这与需求完全相反。正确的条件应该是ElemCompare(...) == 0。
修正后的intersect.c代码
#include "elemtype.h" #include "list.h" Item* Intersect(const Item* i1, const Item* i2) { Item* ris = ListCreateEmpty(); // 遍历第一个链表的每个元素 for (; !ListIsEmpty(i1); i1 = ListGetTail(i1)) { const ElemType* val1 = ListGetHeadValue(i1); // 遍历第二个链表查找匹配元素 for (const Item* tmp2 = i2; !ListIsEmpty(tmp2); tmp2 = ListGetTail(tmp2)) { if (ElemCompare(val1, ListGetHeadValue(tmp2)) == 0) { ris = ListInsertBack(ris, val1); break; // 找到匹配后跳出内层循环,避免重复插入 } } } return ris; } int main(void) { ElemType v1[] = { 0, 1, 2}; Item* i1 = ListCreateEmpty(); for (size_t i = 0; i < 3; i++) { i1 = ListInsertHead(v1 + i, i1); } ElemType v2[] = { 4, 0, 2 }; Item* i2 = ListCreateEmpty(); for (size_t i = 0; i < 3; i++) { i2 = ListInsertHead(v2 + i, i2); } printf("链表i1: "); ListWriteStdout(i1); printf("链表i2: "); ListWriteStdout(i2); Item* i3 = Intersect(i1, i2); printf("交集结果: "); ListWriteStdout(i3); ListDelete(i3); ListDelete(i1); ListDelete(i2); return 0; }
测试验证
运行修正后的代码,输出结果为:
链表i1: [0, 1, 2] 链表i2: [4, 0, 2] 交集结果: [0, 2]
符合预期的交集结果。
内容的提问来源于stack exchange,提问作者Gabriel Burzacchini
相关产品推荐
相关产品推荐

