异质链表(C语言实现)
2015-11-11 22:32
591 查看
用C语言实现多态性的基本思想是使用void*指针,在存储链表结点值的时候将地址存入。
也就是说结点中存储的值并不是具体的值,而是一个地址,由于这个地址是main中的本地变量,所以不用担心它会被摧毁。
在读取的时候,进行强制类型转换即可。
在老师布置异质链表作业的时候,参考了网上很多代码,基本上都是通过传入类型,再在函数内部进行链表创建。这样的写法对于main函数依赖很大,无法实现函数的模块化(也就是说它不能独立存在,必须依赖main函数),个人感觉并不是那么可取。而且链表的值必须通过用户动态输入得到,局限性较大,无法实现传入已有的值。在这一块卡了很久,也查了非常多资料,尤其是void*相关的知识。
不过对于头结点的处理比较麻烦,因为传入链表的时候只能拷贝一个指针,对拷贝的指针进行修改,比如加入头结点,并不会影响到原链表。这时候必须强制用户用返回值来接收新得到的链表指针,或者还有一种解决办法是传入指向链表指针的指针,但是这样也要强迫用户传入指向链表指针的指针。总之非常麻烦,这个问题暂时还没有完美解决。在C++中就很简单,传入引用就可以了。
也就是说结点中存储的值并不是具体的值,而是一个地址,由于这个地址是main中的本地变量,所以不用担心它会被摧毁。
在读取的时候,进行强制类型转换即可。
在老师布置异质链表作业的时候,参考了网上很多代码,基本上都是通过传入类型,再在函数内部进行链表创建。这样的写法对于main函数依赖很大,无法实现函数的模块化(也就是说它不能独立存在,必须依赖main函数),个人感觉并不是那么可取。而且链表的值必须通过用户动态输入得到,局限性较大,无法实现传入已有的值。在这一块卡了很久,也查了非常多资料,尤其是void*相关的知识。
不过对于头结点的处理比较麻烦,因为传入链表的时候只能拷贝一个指针,对拷贝的指针进行修改,比如加入头结点,并不会影响到原链表。这时候必须强制用户用返回值来接收新得到的链表指针,或者还有一种解决办法是传入指向链表指针的指针,但是这样也要强迫用户传入指向链表指针的指针。总之非常麻烦,这个问题暂时还没有完美解决。在C++中就很简单,传入引用就可以了。
/* @fish1996 2015/09/30 */ /* 数字媒体技术1402*/ #include<stdio.h> #include<stdlib.h> enum{Char,Double,Int}; /*链表的声明*/ typedef struct ListNode{ void *data; int type; struct ListNode *next; }*List; /*得到链表的长度*/ int Length(List PtrL); /*创建一个空链表*/ List Create(); /*在表头插入*/ List InsertFro(void *x,int type,List PtrL); /*在第k个位置插入*/ List InsertKth(void *x,int k,int type,List PtrL); /*在表尾插入*/ List InsertBack(void *x,int type,List PtrL); /*查找第k个元素*/ List FindKth(int k,List PtrL); /*查找值为x的元素,返回下标*/ int FindXIndex(void *x,int type,List PtrL); /*查找值为x的元素,返回节点*/ List FindXNode(void *x,int type,List PtrL); /*删除链表头*/ List DeleteFro(List PtrL); /*删除下标为k的元素*/ List DeleteKth(int k,List PtrL); /*删除值为x的元素*/ List Delete(void *x,List PtrL); /*遍历链表输出*/ void Print(List PtrL); /*链表销毁*/ List Destroy(List PtrL); /*两个链表的合并*/ List Union(List p,List s); /*链表的逆置*/ List Reverse(List p); /* 创建一个空的链表 */ List Create() { List L = NULL; /*设立指向NULL的指针*/ return L; } /* 计算链表长度 */ int Length(List PtrL) { int length=0; List p=PtrL; while(p){ /*从第一个结点开始移动指针位置计算长度*/ p=p->next; length++; } return length; } /* 在表头插入 */ /* 使用说明: */ /* 要得到插入表头的链表,只能在返回值中得到 */ /* 因为这里没有传指向指针的指针,原指针并没有被改变 */ /* 传入的是元素x的地址,所以必须是明确定义且赋值的变量*/ /* correct: p=InsertFro(&x,Int,p); */ /* error: p=InsertFro(x,Int,p); */ /* error: InsertFro(&x,Int,p); */ /* error:p=InsertFro(1,Int,p) */ List InsertFro(void *x,int type,List PtrL) { List p; /*申请结点内存并赋值*/ p=(List)malloc(sizeof(struct ListNode)); p->data=x;/*data指针等于x指针*/ p->type=type;/*存储类型*/ p->next=PtrL;/*让该结点指向原链表*/ return p; } /* 在第k个位置插入 */ /* 使用说明: */ /* 要得到插入表头的链表,即k=1时,只能在返回值中得到 */ /* 因为这里没有传指向指针的指针,原指针并没有被改变 */ /* 其余情况可以不使用返回值 */ /* 传入的是元素x的地址,所以必须是明确定义且赋值的变量 */ /* correct: p=InsertKth(&x,1,Int,p); */ /* correct: p=InsertKth(&x,2,Int,p); */ /* correct: InsertKth(&x,2,Int,p); */ /* error: p=InsertKth(x,1,Int,p); */ /* error: InsertKth(&x,1,Int,p); */ /* error:p=InsertKth(1,1,Int,p) */ List InsertKth(void *x,int k,int type,List PtrL) { List p,s; int length; if(k==1)return InsertFro(&x,type,PtrL); /*k=1调用已有函数,实现代码重用*/ p=FindKth(k-1,PtrL);/*找到要删除结点的上一个结点*/ if(!p){ printf("Error: illegal index\n"); return PtrL; }/*不存在上一个结点*/ s=(List)malloc(sizeof(struct ListNode));/*为插入元素分配内存*/ s->next=p->next;/*先让新结点指向它前一个元素的下一个元素*/ p->next=s;/*再让前一个元素指向新节点*/ s->data=x;/*data指针等于x指针*/ s->type=type;/*存储类型*/ return PtrL; } /*在表尾插入 */ /* 使用说明: */ /* 可以不使用返回值 */ /* 传入的是元素x的地址,所以必须是明确定义且赋值的变量 */ /* correct: p=InsertBack(&x,Int,p); */ /* correct: InsertBack(&x,Int,p); */ /* error: p=InsertBack(x,Int,p); */ /* error:p=InsertBack(1,Int,p) */ List InsertBack(void *x,int type,List PtrL) { int k=Length(PtrL); /*得到链表长度*/ return InsertKth(x,k+1,type,PtrL); /*调用已有函数,实现代码重用*/ } /* 查找第k个元素,返回结点 */ /* 使用说明: */ /* correct: s=FindKth(2,p); */ List FindKth(int k,List PtrL) { List p=PtrL; int count=1; while(count!=k&&p){/*当下标不为k并且p不为NULL*/ p=p->next; /*移动结点*/ count++; /*记录当前下标*/ } if(!p){ printf("Error: Not found\n"); } return p; } /*查找值为x的元素,返回下标 */ /* 使用说明: */ /* 由于void*的限制,该函数实现功能较差 */ /* 传入的是元素x的地址,所以必须是明确定义且赋值的变量 */ /* 所以在传入特定值的时候必须先将其定义 */ /* correct: index=FindXIndex(&x,Int,p); */ /* error:index=FindXIndex(1,Int,p) */ int FindXIndex(void *x,int type,List PtrL) { int count=1;/*记录当前下标*/ List p=PtrL; switch(type){ /*选择类型执行对应操作*/ case Int: while(p&&(*(int*)p->data!=*(int*)x||p->type!=Int)){ /*判断p是否为空,值是否相等,类型是否匹配*/ p=p->next; count++; } break; case Double: while(p&&(*(double*)p->data!=*(double*)x||p->type!=Double)){ p=p->next; count++; } break; case Char: while(p&&(*(char*)p->data!=*(char*)x||p->type!=Char)){ p=p->next; count++; } break; } if(!p){ printf("Error: Not found\n"); return 0; } return count; } /*查找值为x的元素,返回结点 */ /* 使用说明: */ /* 由于void*的限制,该函数实现功能较差 */ /* 传入的是元素x的地址,所以必须是明确定义且赋值的变量 */ /* 所以在传入特定值的时候必须先将其定义 */ /* correct: p=FindXNode(&x,Int,p); */ /* error:p=FindXNode(1,Int,p) */ List FindXNode(void *x,int type,List PtrL) { List p; int index; index=FindXIndex(&x,type,PtrL);/*调用已有函数,实现代码重用*/ if(index==0)return NULL; p=FindKth(index,PtrL); return p; } /*删除链表头 */ /* 使用说明: */ /* 要使用返回值得到删除链表头的链表 */ /* 因为这里没有传指向指针的指针,原指针并没有被改变 */ /* correct: p=DeleteFro(p); */ /* error:DeleteFro(p); */ List DeleteFro(List PtrL) { List s=PtrL;/*记录表头位置*/ if(PtrL)PtrL=PtrL->next;/*如果链表不为空,向后移动一位*/ else return PtrL; free(s); /*释放表头空间*/ return PtrL; /*返回删除表头的链表*/ } /*删除下标为k的元素 */ /* 使用说明: */ /* k=1时要使用返回值 */ /* 其余可以使用返回值,也可以不使用 */ /* correct: p=DeleteKth(2,p); */ /* correct:DeleteKth(2,p); */ /* error: DeleteKth(1,p); */ List DeleteKth(int k,List PtrL) { List p,s; if(k==1)return DeleteFro(PtrL);/*调用已有函数,实现代码重用*/ p=FindKth(k-1,PtrL);/*找到要删除结点的前一个结点*/ if(p==NULL||p->next==NULL){ printf("Error: illegal index\n"); return PtrL; } /*如果要删除结点或前一个结点不存在,返回原链表*/ s=p->next;/*s指向待删除结点*/ p->next=s->next;/*待删除结点前一个结点指向待删除结点下一个结点*/ free(s); /*释放待删除结点空间*/ return PtrL; } /*删除链尾 */ /* 使用说明: */ /* 可以使用返回值,也可以不使用 */ /* correct: p=DeleteBack(p); */ /* correct:DeleteBack(p); */ List DeleteBack(List PtrL) { List p; int len=Length(PtrL); return DeleteKth(len,PtrL); } /*遍历链表输出 */ /* 使用说明: */ /* correct: Print(p); */ void Print(List PtrL) { List p=PtrL; while(p){ /*根据类型做对应的输出*/ switch(p->type){ case Int: printf("%d ",*(int*)p->data); break; case Char: printf("%c ",*(char*)p->data); break; case Double: printf("%lf ",*(double*)p->data); break; } p=p->next;/*移动指针位置*/ } printf("\n"); } /*只输出当前结点的值 */ /* 使用说明: */ /* correct: PrintNow(p); */ void PrintNow(List PtrL) { List p=PtrL; if(p){ /*根据类型做对应的输出*/ switch(p->type){ case Int: printf("%d ",*(int*)p->data); break; case Char: printf("%c ",*(char*)p->data); break; case Double: printf("%lf ",*(double*)p->data); break; } } printf("\n"); } /* 链表销毁 */ /* 使用说明: */ /* 要得到销毁后的链表,需要使用返回值 */ /* 因为这里没有传指向指针的指针,原指针并没有被改变 */ /* correct: p=Destroy(p); */ /* error: Destroy(p); */ List Destroy(List PtrL) { List q,p=PtrL; while(p){ q=p->next;/*记录p的下一个结点位置*/ free(p);/*释放p的内存空间*/ p=q;/*p指向下一个结点*/ } return p; } /* 两个链表的合并 */ /* 使用说明: */ /* 按第一个链表在前,第二个链表在后的顺序合并 */ /* 要得到合并后的链表,需要使用返回值 */ /* 因为这里没有传指向指针的指针,原指针并没有被改变 */ /* correct: p=Union(p1,p2); */ /* correct: p1=Union(p1,p2); */ /* error: Union(p1,p2) */ List Union(List PtrL1,List PtrL2) { List p; int len; len=Length(PtrL1);/*得到第一个链表长度*/ p=FindKth(len,PtrL1);/*得到第一个链表的尾结点*/ p->next=PtrL2;/*让第一个链表的尾结点指向第二个链表的头结点*/ return PtrL1; } /* 链表的逆置 */ /* 使用说明: */ /* 要得到逆序后的链表,需要使用返回值 */ /* 因为这里没有传指向指针的指针,原指针并没有被改变 */ /* correct: p=Reverse(p); */ /* error: Reverse(p) */ List Reverse(List PtrL) { List now,next,prev,head,tmp; if(!PtrL)return PtrL; next=PtrL;/*next指向第一个元素*/ now=next->next; /*now指向第二个元素*/ next->next=NULL;/*因为next将会是最后一个结点,将其下一个结点设为NULL*/ while(now){ prev=now->next;/*记录now的下一轮循环的位置*/ now->next=next;/*逆序过程,让后一个结点指向前一个结点*/ next=now;/*next向后移一位*/ now=prev;/*now向后移一位*/ } return next; } int main() { List p=Create(); List l=Create(); int u=1996; int x=1; int y=1; char t='b'; double d=0.1; char a='a'; l=InsertFro(&u,Int,l); printf(">> Insert element 1 in front of the list:\n"); p=InsertFro(&x,Int,p); Print(p); printf("\n"); printf(">> Insert element b with index 2:\n"); InsertKth(&t,2,Char,p); Print(p); printf("\n"); printf(">> Insert element 0.1 at the back of the list:\n"); InsertBack(&d,Double,p); Print(p); printf("\n"); printf(">> The length of the list:\n%d\n",Length(p)); printf("\n"); printf(">> Find the index of element a in the list:\n"); printf("%d\n",FindXIndex(&a,Char,p)); printf("\n"); printf(">> Delete element with index 6:\n"); DeleteKth(6,p); printf("\n"); printf(">> Insert element 1 with index 5:\n"); InsertKth(&x,5,Int,p); printf("\n"); p=Reverse(p); printf(">> After reverse:\n"); Print(p); printf("\n"); List q=FindKth(2,p); printf(">> The second element of the list is:\n"); PrintNow(q); printf("\n"); printf(">> The index of the value 1 is:\n"); printf("%d\n",FindXIndex(&y,Int,p)); printf("\n"); printf(">> Found the element with index 4:\n"); FindKth(4,p); printf("\n"); p=Union(p,l); printf(">> After connect two lists:\n"); Print(p); printf("\n"); p=DeleteFro(p); printf(">> Delete the first element:\n"); Print(p); printf("\n"); DeleteKth(2,p); printf(">> Delete element with index 2:\n"); Print(p); printf("\n"); DeleteBack(p); printf(">> Delete the last element:\n"); Print(p); printf("\n"); p=Destroy(p); printf(">> After destroy:\n"); Print(p); printf("\n"); return 0; }
相关文章推荐
- C++内存泄露心得
- 链表求和(LintCode)
- C++中关于const修饰vector等容器时的问题。
- 【Java】Java调用C/C++程序的实现(jni)
- CEF3开发者系列之外篇——IE中JS与C++交互
- hdoj 1002 a+b 大数相加 C++
- c语言之指针数组理解一
- c++ primer第五版(中文)习题答案 第三章第五节-数组
- 6.c/c++程序员面试宝典-条件语句
- C++11新特性之 Static assertions 和constructor delegation
- C++11新特性之 Static assertions 和constructor delegation
- C++ const限定符总结(C++ primer)
- 欢迎使用CSDN-markdown编辑器
- hdu Flood-it!(IDA*算法)
- 玩转Google开源C++单元测试框架Google Test系列(gtest)之一 - 初识gtest
- 黑马程序员——C语言基础07—指针
- C++容器
- c++ 11 多线线程系列-----thread
- c++学习log1
- 学完《软件工程(C编码实践篇)》之后的总结