c语言实现链表ADT

本程序托管在开源中国http://git.oschina.net/cryt/c_adt

该程序包含list.h头文件,包含结构体以及函数的声明, list.c文件,包含具体函数的实现,demo.c为演示程序。

本链表数据结构如下:

list结构体 表示整体的一个链表

listnode是结点定义

而listnode中的指针pelem指向的是元素的具体内容。

Pelem声明为void *类型。这可以使得链表可以是不同的类型。也就是说,可以在同一个程序中使用不同类型的多个链表。

本程序中链表设置了头结点和尾结点,这给许多操作带来方便。头结点和尾结点并不包含数据。链表初始化的时候,头结点指向尾结点,尾结点指向头结点。头结点的后继是首节点,尾结点的前驱是末节点。头结点和尾结点只是为了方便操作,它们存在于链表的整体定义中,地址保持不变。

 list.h

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

#define BOOL int
#define TRUE 1
#define FALSE 0

//链表结点结构 存储一个结点
typedef struct listnode{
	void * pelem;	//抽象的数据域 可以指向任何类型
	struct listnode * pre;	//指向上一个结点
	struct listnode * next;	//指向下一个结点
} listnode;
//链表结构 储存整个链表
typedef struct list{
	int elemsize;	//链表结点指向的数据域的大小
	int listsize;	//链表中元素的个数
	struct listnode header;	//头结点
	struct listnode trailer;	//尾结点
} list;

list * list_init(int elemsize);	//初始化链表
BOOL getpos_invalid(list * pL, int i);	//判断取值元素的位置是否符合要求
BOOL list_exist(list * pL);	//判断链表是否存在
BOOL list_empty(list * pL);	//判断链表是否为空 若不存在则不为空
BOOL getpos_invalid(list * pL, int i);	//判断元素位置是否合法
BOOL putpos_invalid(list * pL, int i);	//判断要插入的位置是否合法 尾指针算一个
BOOL list_getelem(list * pL, int i, void * pe);	//取第几个元素放到pe指向的结构
listnode * list_getp(list * pL, int i);	//根据位置返回该位置的结点指针,可能是尾指针
BOOL list_insert(list * pL, listnode * p, void * pe);	//在某结点位置前插入结点 可能是尾结点之前
BOOL list_travel(list * pL, void visit());	//遍历链表 执行visit操作
BOOL list_clear(list * pL);	//清空链表
BOOL list_destroy(list ** pL);	//销毁链表
BOOL list_remove(list * pL, listnode * pnode, void *p);	//删除一个元素
int list_length(list * pL);	//判断列表长度
int list_locateelem(list * pL, void * pe, BOOL compare());
BOOL list_priorelem(list * pL, void * pe, void * pre_e);
BOOL list_nextelem(list * pL, void * pe, void * next_e);
BOOL equal(void * a, void * b);

BOOL savelist(list * pL);
BOOL loadlist(list ** pL);

 list.c

//初始化链表
list * list_init(int elemsize)
{
	list * pL;

	if((pL = (list *)malloc(sizeof(list))) == NULL)
		exit(-1);
	pL->elemsize = elemsize;
	pL->listsize = 0;
	pL->header.pelem = NULL;
	pL->header.next = &(pL->trailer);
	pL->header.pre = NULL;
	pL->trailer.pelem = NULL;
	pL->trailer.pre = &(pL->header);
	pL->trailer.next = NULL;

	return pL;
}
//清空链表 链表不存在时 返回错误
BOOL list_clear(list * pL)
{
	listnode * p, *q;
	int i;
	if (list_exist(pL) == FALSE)
		return FALSE;
	if (pL->listsize == 0)
		return TRUE;
	p = pL->header.next;
	i = 1;
	while (i <= pL->listsize)
	{
		free(p->pelem);
		q = p;
		p = p->next;
		free(q);
		i++;
	}
	pL->header.next = &pL->trailer;
	pL->trailer.pre = &pL->header;
	pL->listsize = 0;

	return TRUE;
}
//销毁链表 若本来就不存在 返回成功
BOOL list_destroy(list ** pL)
{
	if (list_exist(*pL) == FALSE)
		return TRUE;
	if (list_clear(*pL) == FALSE)
		return FALSE;
	free(*pL);
	*pL = NULL;
	
	return TRUE;
}
//判断列表是否存在
BOOL list_exist(list * pL)
{
	return (pL == NULL) ? FALSE : TRUE;
}
//判断链表是否为空
BOOL list_empty(list * pL)
{
	if (list_exist(pL) == FALSE)
		return FALSE;
	if (pL->listsize == 0)
		return TRUE;
	return FALSE;
}
//判断位置是否在合理区间 1到元素总数
BOOL getpos_invalid(list * pL, int i)
{
	if (list_exist(pL) == FALSE)
		return FALSE;
	if (i < 1 || i > pL->listsize)
		return FALSE;
	else
		return TRUE;
}
//判断位置是否合理 1到总数+1  尾指针
BOOL putpos_invalid(list * pL, int i)
{
	if (list_exist(pL) == FALSE)
		return FALSE;
	if (i < 1 || i > pL->listsize + 1)
		return FALSE;
	else
		return TRUE;
}
//根据位置返回该位置结点指针 可能是尾指针
listnode * list_getp(list * pL, int i)
{
	int j;
	listnode * p;	//可能指向尾指针
	if (putpos_invalid(pL, i) == FALSE)
		return NULL;
	j = 1;
	p = pL->header.next;
	while (j < i)
	{
		p = p->next;
		j++;
	}
	
	return p;
}
//根据位置把该位置的元素复制到pe指向的内存
BOOL list_getelem(list * pL, int i, void * pe)
{
	int j;
	listnode * p;
	if (getpos_invalid(pL, i) == FALSE)
		return FALSE;
	j = 1;
	p = pL->header.next;
	while (j < i)
	{
		p = p->next;
		j++;
	}
	memcpy(pe, p->pelem, pL->elemsize);
	return TRUE;
}
//插入元素 插入在p结点之前 p可能是尾指针
BOOL list_insert(list * pL, listnode * p, void * pe)
{
	listnode * pnode;

	if((pnode = (listnode *)malloc(sizeof(listnode))) == NULL)
		exit(-1);
	pnode->pelem = malloc(pL->elemsize);
	memcpy(pnode->pelem, pe, pL->elemsize);

	p->pre->next = pnode;
	pnode->pre = p->pre;
	pnode->next = p;
	p->pre = pnode;

	pL->listsize++;
	
	return TRUE;
}
//遍历对元素进行visit()操作
BOOL list_travel(list * pL, void visit())
{
	listnode * p;
	int i;
	if (list_exist(pL) == FALSE)
	{
		printf("链表不存在!");
		return FALSE;
	}
	if (list_empty(pL) == TRUE)
	{
		printf("是空表!");
		return FALSE;
	}
	i = 1;
	p = pL->header.next;
	while (i <= pL->listsize)
	{
		printf("第%d个:\t", i);
		visit(p->pelem);
		p = p->next;
		i++;
	}
	return TRUE;
}
//保存到文件
BOOL savelist(list * pL)
{
	FILE  * out;
	listnode * p;
	int i;
	if (list_exist(pL) == FALSE)
		return FALSE;
	if ((out = fopen("list.dat", "wb")) == NULL)
		return FALSE;
	fwrite(&pL->listsize, sizeof(pL->listsize), 1, out);
	fwrite(&pL->elemsize, sizeof(pL->elemsize), 1, out);
	p = pL->header.next;
	i = 1;
	while (i <= pL->listsize)
	{
		fwrite(p->pelem, pL->elemsize, 1, out);
		p = p->next;
		i++;
	}
	fclose(out);
	return TRUE;
}
//从文件加载
BOOL loadlist(list ** pL)
{
	FILE * in;
	listnode * p, * q;
	int i;
	if (list_destroy(pL) == FALSE)
		return FALSE;
	if ((in = fopen("list.dat", "rb")) == NULL)
		return FALSE;
	if (((*pL) = (list *)malloc(sizeof(list))) == NULL)
		exit(-1);

	fread(&(*pL)->listsize, sizeof((*pL)->listsize), 1, in);
	fread(&(*pL)->elemsize, sizeof((*pL)->elemsize), 1, in);
	(*pL)->header.pelem = NULL;
	(*pL)->header.next = &((*pL)->trailer);
	(*pL)->header.pre = NULL;
	(*pL)->trailer.pelem = NULL;
	(*pL)->trailer.pre = &((*pL)->header);
	(*pL)->trailer.next = NULL;

	
	i = 1;
	q = &((*pL)->header);
	while (i <= (*pL)->listsize)
	{
		if((p = (listnode *)malloc(sizeof(listnode))) == NULL)
			exit(-1);
		p->pelem = malloc((*pL)->elemsize);
		fread(p->pelem, (*pL)->elemsize, 1, in);
		q->next = p;
		p->pre = q;
		q = p;
		i++;
	}
	q->next = &(*pL)->trailer;
	(*pL)->trailer.pre = q;

	return TRUE;
}
//删除单个元素 根据元素内容*pnode找到该节点 并将删除的元素放到p
BOOL list_remove(list * pL, listnode * pnode, void *p)
{
	if (list_exist(pL) == FALSE || list_empty(pL) == TRUE)
		return FALSE;
	pnode->pre->next = pnode->next;
	pnode->next->pre = pnode->pre;
	memcpy(p, pnode->pelem, pL->elemsize);
	free(pnode);
	pL->listsize--;

	return TRUE;
}
//返回列表有效元素个数
int list_length(list * pL)
{
	if (list_exist(pL) == TRUE)
		return pL->listsize;
	else
		return -1;
}
//根据规则查找列表中第一个满足要求的元素 放到pe中
int list_locateelem(list * pL, void * pe, BOOL compare())
{
	listnode * p;
	int i;
	p = pL->header.next;
	i = 1;
	while (i <= pL->listsize)
	{
		if (compare(p->pelem, pe) == TRUE)
			return i;
		p = p->next;
		i++;
	}
	return 0;	
}
//求前驱元素 
BOOL list_priorelem(list * pL, void * pe, void * pre_e)
{
	int i;
	if (list_exist(pL) == FALSE || list_empty(pL) == TRUE)
		return FALSE;
	if((i = list_locateelem(pL, pe, &equal)) == 0 || i == 1)
		return FALSE;
	if (list_getelem(pL, i-1, pre_e) == TRUE)
		return TRUE;
	else
		return FALSE;
}
//求后继元素
BOOL list_nextelem(list * pL, void * pe, void * next_e)
{
	int i;
	if (list_exist(pL) == FALSE || list_empty(pL) == TRUE)
		return FALSE;
	if((i = list_locateelem(pL, pe, &equal)) == 0 || i == pL->listsize)
		return FALSE;
	if (list_getelem(pL, i+1, next_e) == TRUE)
		return TRUE;
	else
		return FALSE;
}

 demo.c

#include "list.h"
#include "list.c"

#define ElemType student
typedef struct {
	char name[20];
	int age;
} student;

void display(void *stu);
void menu(void);
void inputstu(student * p);	//输入学生信息
void inputindex(int *);	//输入位置信息


int main(void){
	list * pL;
	student * pe;
	listnode * pnode;
	student pre_e, next_e;
	int op;
	int i;
	op = 0;
	pL = NULL;
	if ((pe = (student *)malloc(sizeof(student))) == NULL)
		exit(-1);
	do{
		 system("cls");
		 menu();
		 printf("          Please input your option[0-12]:");
		 scanf("%d",&op);
		 switch(op){
			 case 0: break;
			 case 1: //初始化列表
				 if (list_exist(pL) == TRUE || list_empty(pL) == FALSE)
					 list_destroy(&pL);
				 if ((pL = list_init(sizeof(student))) != NULL)
				 {
					 printf("初始化成功!");
					  getchar();getchar();
				 }
				 else
				 {
					 printf("初始化失败!");
					  getchar();getchar();
				 }			 break;
			 case 2: //插入元素
				 inputindex(&i);
				 inputstu(pe);

				 if ((pnode = (list_getp(pL, i))) == NULL)
				 {
					 printf("位置不对或者链表不存在!");
					 getchar();getchar();
					 break;
				 }
				 if (list_insert(pL, pnode, pe) == TRUE)
					 printf("插入成功!");
				 else
					 printf("插入失败!");
				 getchar();getchar();
							 break;
			 case 3: //销毁列表
				 if(list_destroy(&pL) == TRUE)
					 printf("销毁成功!");
				getchar();getchar();
				break;
			 case 4: //清空列表
				 if(list_clear(pL) == TRUE)
				 {
					 printf("清空成功!");
					getchar();getchar();
				 }
				 else
				 {
					printf("列表不存在!");
				 	getchar();getchar();
				 }
				break;
		/*	 case 5: //删除区间
				 printf("请输入区间");
				 scanf("%d %d", &lo, &hi);
				 if(list_delete(pL, lo, hi) == FALSE)
				 {
					 printf("删除失败");
					getchar();getchar();
				 }
				 else
				{
					 printf("删除成功!");
					getchar();getchar();
				 }
				 break;*/
			 case 6: //删除某元素
				inputindex(&i);
				if (getpos_invalid(pL, i) == FALSE)
				{
					printf("位置不合法或列表不存在");
					getchar();getchar();
					break;
				}
				if ((pnode = list_getp(pL, i)) == NULL)
				{
					printf("删除失败!");
					getchar();getchar();
					break;
				}
				if(list_remove(pL, pnode, pe) == TRUE)
				{
					printf("第%d个元素", i);
					display(pe);
					printf("删除成功");
					getchar();getchar();
				}
				else
				{
					printf("删除失败!");
					getchar();getchar();
				}
							 
							 break;
			 case 7: //判断列表是否存在
				 if (list_exist(pL) == TRUE)
				 {
					 printf("列表存在!");
					 getchar();getchar();
				 }
				 else
				 {
					 printf("列表不存在!");
					 getchar();getchar();
				 }
				break;
			 case 8: //判断列表是否为空
				 if (list_exist(pL) == FALSE)
				 {
					 printf("列表不存在!");
					getchar();getchar();
					break;
				 }
				if (list_empty(pL) == TRUE)
				 {
					 printf("列表为空!");
					 getchar();getchar();
				 }
				 else
				 {
					 printf("列表不为空!");
					 getchar();getchar();
				 }
				 break;
			 case 9: //遍历元素
				 list_travel(pL, &display);
							 getchar();getchar();
							 break;
			 case 10: //求列表长度
				 if (list_exist(pL) == FALSE)
				 {
					 printf("列表不存在!");
					getchar();getchar();
					break;
				 }
				 printf("列表长度是%d", list_length(pL));
					getchar();getchar();
				break;
			 case 11: //按位置查找元素
				 inputindex(&i);
				if (getpos_invalid(pL, i) == FALSE)
				{
					printf("位置不合法或列表不存在");
					getchar();getchar();
					break;
				}
				if ((pnode = list_getp(pL, i)) == NULL)
				{
					printf("失败!");
					getchar();getchar();
					break;
				}
				display(pnode->pelem);
				getchar();getchar();
				break;
			 case 12: //按元素查找位置
				 inputstu(pe);
				 if ((i = list_locateelem(pL, (void *)pe, &equal)) == 0)
				 {
					 printf("查找失败!");
					getchar(); getchar();
				 }
				 else
				 {
					 printf("是第%d个元素", i);
					 getchar(); getchar();
				 }
								 break;
			 case 13: //求前驱元素
				 inputstu(pe);
				 if(list_priorelem(pL, pe, &pre_e) == TRUE)
				 {
					 display(pe);
					 printf("的前驱元素是");
					 display(&pre_e);
					getchar(); getchar();
				 }
				 else
				 {
					 printf("查找前驱元素失败!");
					 getchar(); getchar();
				 }
								 break; 
			 case 14: //求后继元素
				 	inputstu(pe);
				 if(list_nextelem(pL, pe, &next_e) == TRUE)
				 {
					 display(pe);
					 printf("的后继元素是");
					 display(&next_e);
					getchar(); getchar();
				 }
				 else
				 {
					 printf("查找后继元素失败!");
					 getchar(); getchar();
				 }
								 break;
			 case 15: //保存到文件
				 if(savelist(pL) == TRUE)
				 {
					 printf("保存成功!");
					 getchar(); getchar();
				 }
				 else
				 {
					 printf("保存失败!");
					 getchar(); getchar();
				 }
				break;
			 case 16: //从文件加载
				  if(loadlist(&pL) == TRUE)
				  {
					 printf("加载成功!");
					 getchar(); getchar();
				  }
				  else
				  {
					 printf("加载失败!");
					 getchar(); getchar();
				  }
				 break;							
			 default: ;
		 }
	}while(op);
	printf("\n--------------------Welcome again!--------------------\n");
	getchar();getchar();

	return 0;
}

void display(void *stu)
{
	printf("姓名:%s\t年龄%d\n", ((student *)stu)->name, ((student *)stu)->age);
}

void menu(void){
	printf("\n\n");
	printf("      Menu for Linear Table On Sequence Structure \n");
	printf("------------------------------------------------------\n");
	printf("\t修改类\t\t\t查找类\n");
	printf("\t1. 初始化列表\t\t7. 判断列表是否存在\n");
	printf("\t2. 插入元素\t\t8. 判断列表是否为空\n");
	printf("\t3. 销毁列表\t\t9. 遍历列表元素\n");
	printf("\t4. 清空列表\t\t10. 列表长度\n");
	printf("\t5. 删除区间\t\t11. 按位置查找元素\n");
	printf("\t6. 删除某元素\t\t12. 按元素查找位置\n");
	printf("\t\t\t\t13. 求某元素前驱元素\n");
	printf("\t\t\t\t14. 求某元素后继元素\n");
	printf("\t文件类");
	printf("\n\t15. Savelist\t\t16. Loadlist\n");
	printf("    	  0. Exit\n");
	printf("------------------------------------------------------\n");
	
}

void inputstu(student * p)
{
	printf("请输入学生姓名:");
	scanf("%s", p->name);
	printf("请输入年龄:");
	scanf("%d", &p->age);
	
}
void inputindex(int * i)
{
	printf("请输入位置:");
	scanf("%d", i);
}

//判断两个元素是否相等
BOOL equal(void * a, void * b)
{
	student * p1 , * p2;
	p1 = a;
	p2 = b;
	if ((strcmp(p1->name, p2->name) == 0) && p1->age == p2->age)
		return TRUE;
	else
		return FALSE;
}

如果文章对你有帮助,欢迎点赞或打赏(金额不限)。你的打赏将全部用于支付网站服务器费用和提高网站文章质量,谢谢支持。

版权声明:

本文由 原创,商业转载请联系作者获得授权。
非商业转载请注明作者 雅乐网 ,并附带本文链接:
https://www.yalewoo.com/c_adt_list.html

上一篇:

下一篇:

我要评论

验证码*: 7 + 8 =