Skip to content

Latest commit

 

History

History
310 lines (303 loc) · 9.83 KB

File metadata and controls

310 lines (303 loc) · 9.83 KB

一起talk C栗子吧(第十五回:C语言实例--双向链表)

各位看官们,大家好,从今天开始,我们讲大型章回体科技小说 :C栗子,也就是C语言实例。闲话休提, 言归正转。让我们一起talk C栗子吧!

看官们,上一回中咱们说的是循环链表的例子,这一回咱们说的例子是:双向链表。

看官们,双向链表也是一种链表。我们在前面两回中说到的链表,都是沿着链表头部到链表尾部这样的方 向进行操作,而今天咱们要说的双向链表既可以沿着链表头部到链表尾部这样的方向进行操作,也可以沿 着链表尾部到链表头部这样的方向进行操作。这也是正是叫它双向链表的原因。

在例子中,查找和删除结点的函数中,可以使用沿着链表尾部向链表头部这样的方向进行查找或者删除, 当然了,也可以向单链表一样沿着链表头部到链表尾部这样的方向进行查找或者删除。不论沿着哪个方向 操作,只需要关注该方向的指针就可以。比如,沿着链表头部到链表尾部这样的方向进行相关的操作,只 需要关注结点中的next指针就行,pre指针可以忽略。但是删除操作需要注意,因为它会从链表中删除一 个符合要求的结点,删除的时候需要把该结点的pre和next指针都处理好,不然会破坏链表的完成性,在 删除操作后,如果再进行遍历操作,就会因为链表不完整,遍历不到链表中的所有结点,也就是形成了断 链,这是最严重的错误。我在代码中有中文注释。

看官们,详细的代码如下,大家可以参考:

     1	/* **************************
     2	 * Head file of Double Linked List
     3	 * *************************/
     4	#ifndef DOUBLE_LINKED_LIST_H
     5	#define DOUBLE_LINKED_LIST_H
     6	
     7	#include<stdio.h>
     8	#include<stdlib.h>
     9	
    10	//#define DEBUG 0
    11	#ifdef DEBUG
    12	int size = 0;
    13	#endif
    14	
    15	#define SUCCESS 0
    16	#define FAILE 1
    17	
    18	typedef int ListElmt; //把int当作List类型,实际中可以使用其它的类型或者自己定义一个List类型,不过不能是使用struct复合类型
    19	typedef struct _ListNode  //List node 类型
    20	{
    21		ListElmt data;
    22		struct _ListNode *pre;
    23		struct _ListNode *next;
    24	}ListNode;
    25	
    26	//typedef struct _ListNode ListNode;
    27	typedef struct _ListNode *List; //定义链表的类型为ListNode类型的指针
    28	
    29	//声明函数原型,这里有插入,删除,查找链表元素的函数,以及遍历链表的函数
    30	int ListCreate(List *list,int len);
    31	int ListDestroy(List *list);
    32	int ListInsert(List *list,ListNode *node);
    33	int ListDelete(List *list,ListElmt data);
    34	int ListTravel(List list);
    35	int ListFindElement(List list,ListElmt data);
    36	
    37	#endif /*DOUBLE_LINKED_LIST_H*/
     1	/* **************************
     2	 * Soure file of Double Linked List
     3	 * *************************/
     4	
     5	#include"DoubleLinkedList.h"
     6	
     7	//实现List的函数,为了方便,放到了主函数所在的文件中,最好是单独建立实现函数的源文件
     8	
     9	//创建List Node,创建一个长度为len的链表,其中有一个链表头,链表中结点个数为len
    10	int ListCreate(List *list,int len)
    11	{
    12		ListNode *l = NULL;
    13		ListNode *p = NULL;
    14	
    15		l = (ListNode*)malloc(sizeof(ListNode)); //先创建头结点
    16	
    17		if(NULL == l) //分配成功后才能对它进行操作
    18			return FAILE;
    19	
    20		*list = l; //创建头结点,并且初始化
    21		l->data = 0;
    22		l->pre = NULL;
    23		l->next = NULL;
    24	#ifdef DEBUG
    25		size += 1;
    26	#endif
    27	
    28		while(len-- > 0 )
    29		{
    30			p = (ListNode*)malloc(sizeof(ListNode)); //新创建结点
    31	
    32			if(NULL != p)
    33			{
    34				l->next = p;
    35				p->pre = l;
    36				p->next = NULL;
    37				l = p;
    38				l->data = 0; //结点中的元素初始化为0,这里的data是Int类型
    39			}
    40	#ifdef DEBUG
    41		size += 1;
    42	#endif
    43		}
    44	
    45		return SUCCESS;
    46	}
    47	
    48	//删除List,从链表头开始删除,每次删除一个结点,直到所有结点都删除为止,此时为空链表,只剩下头结点
    49	int ListDestroy(List *list)
    50	{
    51		ListNode *l = NULL;
    52		ListNode *p = NULL;
    53	
    54		if(NULL == *list) //空链表时不进行删除操作
    55		{
    56			printf("the list is empty \n");
    57			return FAILE;
    58		}
    59	
    60		l = (*list)->next;
    61		free(*list); //释放头结点
    62		*list = NULL;
    63	
    64	#ifdef DEBUG
    65		size -= 1;
    66		printf(" a delete a node in the list \n");
    67	#endif
    68		while(NULL != l)  //从链表头结点的下一个结点开始,一个结点接着一个结点地删除
    69		{
    70			p = l->next; //detroy循环链表,沿着一个方向遍历就行,不需要考虑pre,但是delete链表时需要考虑,因为只是删除链表中的一个结点,不考虑会影响链表的完成性。
    71			free(l);
    72			l = p;
    73	#ifdef DEBUG
    74		size -= 1;
    75		printf("delete a node in the list \n");
    76	#endif
    77		}
    78		return SUCCESS;
    79	}
    80	
    81	//向链表中插入结点,插入时从链表尾部插入,每次插入一个结点.
    82	int ListInsert(List *list,ListNode *node)
    83	{
    84		ListNode *l = NULL;
    85	
    86		if(NULL == *list)
    87		{
    88			printf("this is a empty list, can't be insered \n");
    89			return FAILE;
    90		}
    91	
    92		l = (*list)->next;
    93	//	while(l != *list) //遍历链表到表尾
    94	//		l = l->next;
    95	
    96	//	l = node; //在表尾插入结点
    97	//	l->next =*list;
    98	
    99		(*list)->next = node; //在表头,也就是头结点后面插入结点,省去遍历链表的时间,这是有头结点的好处
   100		node->pre = l->pre;
   101		l->pre = node;
   102		node->next = l;
   103	
   104	#ifdef DEBUG
   105		size += 1;
   106	#endif
   107		return SUCCESS;
   108	}
   109	
   110	//删除链表中包含data的结点
   111	int ListDelete(List *list,ListElmt data)
   112	{
   113		int flag = 0;
   114		int result = FAILE;
   115		ListNode  *current = NULL;
   116		ListNode *next = NULL;
   117	
   118		if(NULL == *list) //空链表没有结点,不能删除结点
   119		{
   120			printf("this is a empty list, can't be deleted \n");
   121			return FAILE;
   122		}
   123	
   124		current = *list;
   125		next = (*list)->next;
   126	
   127		while(NULL != next) //依次遍历链表,查找被删除的元素,如果找到则删除结点,并且停止遍历链表
   128		{
   129			if(data == next->data)
   130			{
   131				current->next = next->next;
   132				(next->next)->pre = next->pre;
   133				free(next);
   134				next = NULL;
   135				flag = 1;
   136	#ifdef DEBUG
   137		size -= 1;
   138	#endif
   139				break;
   140			}
   141			current = next; //这里只沿一个方向进行遍历就可以
   142			next = next->next;
   143		}
   144	
   145		if( 1 == flag )
   146			result = SUCCESS;
   147		else
   148			result =  FAILE;
   149	
   150		return result;
   151	}
   152	
   153	//遍历链表,显示链表中的每个结点的data
   154	int ListTravel(List list)
   155	{
   156		ListNode *l = NULL;
   157	
   158		if(NULL == list) //空链表直接返回
   159		{
   160			printf("It is a empyt list \n");
   161			return FAILE;
   162		}
   163	
   164		l = list->next;
   165	
   166		while(l != NULL) //遍历链表,并且显示链表中的data
   167		{
   168			printf("%d \n",l->data);
   169			l = l->next;
   170		}
   171	
   172	#ifdef DEBUG
   173		printf("list size: %d \n",size);
   174	#endif
   175		return SUCCESS;
   176	}
   177	
   178	//查找链表中是否包含值为data的结点
   179	int ListFindElement(List list,ListElmt data)
   180	{
   181		ListNode *l = NULL;
   182		int result = FAILE;
   183	
   184		if(NULL == list) //空链表直接返回
   185		{
   186			printf("It is a empyt list \n");
   187			return FAILE;
   188		}
   189	
   190		l = list->next;
   191		while( NULL != l) //依次遍历链表,查找值为data的结点
   192		{
   193			if(data == l->data) //找到后停止遍历链表,否则继续遍历链表
   194			{
   195				result = SUCCESS;
   196				break;
   197			}
   198			else
   199				l = l->next;
   200		}
   201	
   202		return result;
   203	}
   204	
   205	int main()
   206	{
   207		int result  = FAILE;
   208		int len = 3;
   209		ListNode *node = NULL; //node必须是指针,而且要用malloc分配空间,因为要使free释放
   210		List list = NULL; //创建一个指向链表的空指针
   211	
   212		node = (ListNode *)malloc(sizeof(ListNode));
   213		if(NULL != node)
   214		{
   215			node->data = 1;
   216			node->next = NULL;
   217			node->pre = NULL;
   218		}
   219	
   220		result = ListCreate(&list,len); //创建一个长度为Len的链表
   221		if(result == SUCCESS)
   222			ListTravel(list);
   223	
   224		printf("Insert a node into list \n");
   225		ListInsert(&list,node);
   226		ListTravel(list);
   227		
   228		result = ListFindElement(list,0);
   229		if(result == SUCCESS )
   230			printf("find it in the list \n");
   231		else
   232			printf("don't find it in the list \n");
   233	
   234		printf("main delete a node in list \n");
   235		result = ListDelete(&list,0);
   236		if(result == SUCCESS )
   237			ListTravel(list);
   238	
   239		ListDestroy(&list);
   240	
   241	#ifdef DEBUG
   242		printf("list size: %d \n",size);
   243	#endif
   244		ListTravel(list);
   245		return result;
   246	}

各位看官,关于双向链表的例子咱们就说到这里。欲知后面还有什么例子,且听下回分解。