异质链表(C语言实现)

  用C语言实现多态性的基本思想是使用void*指针,在存储链表结点值的时候将地址存入。

  也就是说结点中存储的值并不是具体的值,而是一个地址,由于这个地址是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;
}


  • 8
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值