用C语言实现多态性的基本思想是使用void*指针,在存储链表结点值的时候将地址存入。
也就是说结点中存储的值并不是具体的值,而是一个地址,由于这个地址是main中的本地变量,所以不用担心它会被摧毁。
在读取的时候,进行强制类型转换即可。
在老师布置异质链表作业的时候,参考了网上很多代码,基本上都是通过传入类型,再在函数内部进行链表创建。这样的写法对于main函数依赖很大,无法实现函数的模块化(也就是说它不能独立存在,必须依赖main函数),个人感觉并不是那么可取。而且链表的值必须通过用户动态输入得到,局限性较大,无法实现传入已有的值。在这一块卡了很久,也查了非常多资料,尤其是void*相关的知识。
不过对于头结点的处理比较麻烦,因为传入链表的时候只能拷贝一个指针,对拷贝的指针进行修改,比如加入头结点,并不会影响到原链表。这时候必须强制用户用返回值来接收新得到的链表指针,或者还有一种解决办法是传入指向链表指针的指针,但是这样也要强迫用户传入指向链表指针的指针。总之非常麻烦,这个问题暂时还没有完美解决。在C++中就很简单,传入引用就可以了。
/* @fish1996 2015/09/30 */
/* 数字媒体技术1402*/
#include
#include
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;
}