2.3 线性表的链式表示和实现(双向循环链表)

盛年不重来,一日难再晨。及时宜自勉,岁月不待人。

                                                                                      ——陶渊明

目录

一、前言

二、双向循环链表图示

三、双链表的存储结构

四、常用操作的实现

1.头插法建立双向循环链      图示   代码      2.尾插法建立双向循环链表    图示    代码                                       3.查找结点     

4.获取链表长度                                           5.删除第i个结点     图示    代码       6.在第i个位置之前插入新结点    图示    代码

7.输出链表                                                  8.修改第i个结点的值                       9.销毁链表                               10.清空链表

五、源代码

六、总结


 

一、前言

学习了单链表,我们发现每个结点只能找到其后继结点,而不能找到前驱结点,我们可以思考能否再加一个指针域,使其能够访问其前驱结点,这就是双链表的由来。因此双向循环链表的意思就是一个结点有两个指针域,分别指向直接前驱结点和直接后继结点,并且尾结点指向头结点,这样的好处是什么呢?

好处就是可以直接访问某个结点的前驱结点,并且由任意一个结点出发均能找到表中所有结点。这在单链表中是不能实现的。应用在双向查询的场合,但双向循环链表的缺点就是占用内存大,而且操作稍微复杂。

二、双向循环链表图示

双向循环链表图示

三、双链表的存储结构

typedef struct DuLNode{
	int            data;
	struct DuLNode * prior;//前驱结点 
	struct DuLNode * next; //后继结点 
}DuLNode,* DuLinkList;

/*  最后一句的等同于下面代码,中括号是我自己加上的,只是提示把他们看作是一个整体
 
typedef  {struct  DuLNode   }  DuLNode;      // struct  DuLNode 的别名是 DuLNode
 
typedef  {struct  DuLNode * }  DuLinkList;   // struct  DuLNode *  的别名是 DuLinkList

*/

四、常用操作的实现

1.头插法建立双向循环链表

图示

头插法建立双向循环链表

代码


 //1.头插法建立线性表 
 Status create_headLinked_List(DuLinkList & L,int n) 
 //逆序输入n个元素的值,建立带表头结点双向循环链表L 
 {
 	DuLinkList head = (DuLinkList)malloc(sizeof(DuLNode));//头结点的存储地址 
 	if(NULL == head) return ERROR;                        //分配空间失败 
 	L = head;                                             //头指针L存储头结点P的地址,即头指针指向头结点 
 	L->data = 0; 					      //用头结点统计元素个数,刚开始为0个
	L->prior = L;                                	      //链表为空,头结点prior指向自己 
 	L->next  = L;                             	      //链表为空,头结点next指向自己  
 							      //由此得出链表为空条件: L->next == L->prior == L 
 	for(int i = 0;i<n;i++)
	 {
 		DuLinkList List_Node = (DuLinkList)malloc(sizeof(DuLNode));
 		if(NULL == List_Node) return ERROR;
 		scanf("%d",&List_Node->data);
 		L->data++;  
		List_Node->prior  = head;                         
 		List_Node->next   = head->next;
		head->next->prior = List_Node;                //头插法,即链表中的元素顺序与输入相反 
 		head->next = List_Node;
	 } 
	 return OK;
 }
 

2.尾插法建立双向循环链表

图示

  

代码

//2.尾插法创建带头结点的链表 
 Status tail_create_heahLinked_List(DuLinkList & L,int n) 
 {
 	DuLinkList head = (DuLinkList)malloc(sizeof(DuLNode));//头结点的存储地址 
 	if(NULL == head) return ERROR;                       
        L = head;                                             //头指针L存储头结点P的地址,即头指针指向头结点 
 	L->data = 0; 					      //用头结点统计元素个数,刚开始为0个
	L->prior = L;                                		  
 	L->next  = L;                             		      
	DuLinkList List_Node = NULL;                               
 	for(int i = 0;i<n;i++)
	 {
 		List_Node = (DuLinkList)malloc(sizeof(DuLNode));
 		if(NULL == List_Node) return ERROR;
 		scanf("%d",&List_Node->data);
 		L->data++;  
		List_Node->prior  = head->prior;
		List_Node->next   = head;               //每插入一个元素,头结点统计链表元素个数加一 
 		head->prior->next = List_Node;          //尾插法,即链表中的元素顺序与输入相同 
 		head->prior = List_Node;
	 } 
	 return OK;
 }

3.查找结点

 

//3.查找某一个数据是否在链表中,如果没有,返回false,否则返回该结点的位序
 
 Status  getDuLNodeIndex(DuLinkList L,int number)
 {
 	int i=1;
 	DuLinkList  temp =L->next;
 	while(temp != L ) 
	 {
 		if(number == temp->data) return i;
 		temp = temp->next;
 		i++;
	 }
	 return FALSE;
 }

 

4.获取链表长度

 //4.获取链表长度
 Status getLength(DuLinkList L)
 {
 	return L->data;
 } 

 

5.删除第i个结点

图示

代码

//5.删除某个位置节点 (从1开始) 
 
 Status  deleteDuLNode(DuLinkList L ,int i,int & e)       //用e保存删除的节点 
 {
 	int j=1;
 	if(L->next == L && L->prior ==L )  return ERROR;  //表为空 
 	if(j > i || i>L->data)  return ERROR;             //说明位置不合理,直接返回 
 	DuLinkList temp = L;
	while(j<=i)
	{
		temp = temp->next;
		j++;
	} 
	e = temp->data; 
        temp->next->prior = temp->prior;
	temp->prior->next = temp->next; 
	free(temp);
	L->data--;
	return OK;
 } 
 

6.在第i个位置之前插入新结点

图示

代码

//6.在第i个位置之前插入新结点
Status  insertDuLNode(DuLinkList L ,int i,int value)
{
	int j=1;
	if( j > i || i>L->data + 1 )   return ERROR;//允许在最后一个元素之后插入元素 
	DuLinkList temp = L;
	DuLinkList newDuLNode = (DuLinkList)malloc(sizeof(DuLNode));
	while(j<i)
	{
		temp = temp->next;
		j++;
	}
	newDuLNode->data  = value;
	newDuLNode->next  = temp->next;
	newDuLNode->prior = temp;
	temp->next->prior = newDuLNode;
	temp->next = newDuLNode;
	L->data++;
	return OK;
} 

7.输出链表

//7.输出带头结点链表中的元素 
 void headlinked_List_Traverse(DuLinkList  L)
 {
 	
    printf("该链表的元素个数为:%d \n",L->data);
	DuLinkList p = L->next;    
 	while(p != L)
	{    
 	    printf(" %d ",p->data);	
		p = p->next;	    	
	}
 	
}

 

8.修改第i个结点的值

//8.修改第i个结点的值

Status  updateDuLNode(DuLinkList L,int i,int value)
{
	int j=1;
 	if(L->next == L && L->prior ==L )  return ERROR;//表为空 
 	if(j > i || i>L->data)  return ERROR;           //说明位置不合理,直接返回 
	DuLinkList temp = L;
	while(j<=i )
	{
		temp = temp->next;
		j++;
	}  
        temp->data = value; 
	return OK;
} 

9.销毁链表

//销毁链表,包括头结点的所有结点全部释放	 
Status destroy_headlinked_list(DuLinkList & L)	
{
	L->data = 0; 
	DuLinkList p = NULL;
	while(L)
	{   
	    p = L->next;
	    free(L);
	    L = p;			    	
	} 
        L = NULL;
 	return OK;
} 

 

10.清空链表

Status clear_headlinked_list(DuLinkList  L)	
{
	L->data = 0; 
	DuLinkList p,q;
	p = q = L->next;
	while(p != L)
	{   
	    q = p->next;
	    free(p);
	    p = q;			    	
	} 
	L->next = L->prior = L;
 	return OK;
 
}  

 

五、源代码

#include<cstdio>
#include<cstdlib>
#include<malloc.h>
#include<cstring>
#include<conio.h>
//函数结果状态代码 
#define TRUE  1
#define FALSE 0
#define OK    1
#define ERROR 0 

typedef int Status;  //int 的别名,其值是函数结果状态代码 

typedef struct DuLNode{
	int            data;
	struct DuLNode * prior;//前一个结点 
	struct DuLNode * next; //后一个结点 
}DuLNode,* DuLinkList;

/*  最后一句的等同于下面代码,中括号是我自己加上的,只是提示把他们看作是一个整体
 
typedef  {struct  DuLNode   }  DuLNode;      // struct  DuLNode 的别名是 DuLNode
 
typedef  {struct  DuLNode * }  DuLinkList;   // struct  DuLNode *  的别名是 DuLinkList

*/


 //1.建立线性表 
 Status create_headLinked_List(DuLinkList & L,int n) 
 //逆序输入n个元素的值,建立带表头结点双向循环链表L 
 {
 	DuLinkList head = (DuLinkList)malloc(sizeof(DuLNode));//头结点的存储地址 
 	if(NULL == head) return ERROR;                        //分配空间失败 
 	L = head;                                       	  //头指针L存储头结点P的地址,即头指针指向头结点 
 	L->data = 0; 										  //用头结点统计元素个数,刚开始为0个
	L->prior = L;                                		  //链表为空,头结点prior指向自己 
 	L->next  = L;                             		      //链表为空,头结点next指向自己  
 													      //由此得出链表为空条件: L->next == L->prior == L 
 	
	head->next = head;									
 	head->prior = head;
	 for(int i = 0;i<n;i++)
	 {
 		DuLinkList List_Node = (DuLinkList)malloc(sizeof(DuLNode));
 		if(NULL == List_Node) return ERROR;
 		scanf("%d",&List_Node->data);
 		L->data++;  
		List_Node->prior  = head;                         
 		List_Node->next   = head->next;
		head->next->prior = List_Node;                    //头插法,即链表中的元素顺序与输入相反 
 		head->next = List_Node;
	 } 
	 return OK;
 }
 
 //2.尾插法创建带头结点的链表 
 Status tail_create_heahLinked_List(DuLinkList & L,int n) 
 {
 	DuLinkList head = (DuLinkList)malloc(sizeof(DuLNode));//头结点的存储地址 
 	if(NULL == head) return ERROR;                       
    L = head;                                       	  //头指针L存储头结点P的地址,即头指针指向头结点 
 	L->data = 0; 										  //用头结点统计元素个数,刚开始为0个
	L->prior = L;                                		  
 	L->next  = L;                             		      
	DuLinkList List_Node = NULL;                               
 	for(int i = 0;i<n;i++)
	 {
 		List_Node = (DuLinkList)malloc(sizeof(DuLNode));
 		if(NULL == List_Node) return ERROR;
 		scanf("%d",&List_Node->data);
 		L->data++;  
		List_Node->prior  = head->prior;
		List_Node->next   = head;               //每插入一个元素,头结点统计链表元素个数加一 
 		head->prior->next = List_Node;               //尾插法,即链表中的元素顺序与输入相同 
 		head->prior = List_Node;
	 } 
	 return OK;
 }
 
 
 //3.查找某一个数据是否在链表中,如果没有,返回false,否则返回该结点的位序
 
 Status  getDuLNodeIndex(DuLinkList L,int number)
 {
 	int i=1;
 	DuLinkList  temp =L->next;
 	while(temp != L ) 
	 {
 		if(number == temp->data) return i;
 		temp = temp->next;
 		i++;
	 }
	 return FALSE;
 }
 
 //4.获取链表长度
 Status getLength(DuLinkList L)
 {
 	return L->data;
 } 
 
 //5.删除某个位置节点 (从1开始) 
 
 Status  deleteDuLNode(DuLinkList L ,int i,int & e)//用e保存删除的节点 
 {
 	int j=1;
 	if(L->next == L && L->prior ==L )  return ERROR;//表为空 
 	if(j > i || i>L->data)  return ERROR;//说明位置不合理,直接返回 
 	DuLinkList temp = L;
	while(j<=i)
	{
		temp = temp->next;
		j++;
	} 
	e = temp->data; 
    temp->next->prior = temp->prior;
	temp->prior->next = temp->next; 
	free(temp);
	L->data--;
	return OK;
 } 
 

  
//6.在第i个位置之前插入新结点
Status  insertDuLNode(DuLinkList L ,int i,int value)
{
	int j=1;
	if( j > i || i>L->data + 1 )   return ERROR;//允许在最后一个元素之后插入元素 
	DuLinkList temp = L;
	DuLinkList newDuLNode = (DuLinkList)malloc(sizeof(DuLNode));
	while(j<i)
	{
		temp = temp->next;
		j++;
	}
	newDuLNode->data  = value;
	newDuLNode->next  = temp->next;
	newDuLNode->prior = temp;
	temp->next->prior = newDuLNode;
	temp->next = newDuLNode;
	L->data++;
	return OK;
} 



 //7.输出带头结点链表中的元素 
 void headlinked_List_Traverse(DuLinkList  L)
 {
 	
    printf("该链表的元素个数为:%d \n",L->data);
	DuLinkList p = L->next;    
 	while(p != L)
	{    
 	    printf(" %d ",p->data);	
		p = p->next;	    	
	}
 	
}

//8.修改第i个结点的值

Status  updateDuLNode(DuLinkList L,int i,int value)
{
	int j=1;
 	if(L->next == L && L->prior ==L )  return ERROR;//表为空 
 	if(j > i || i>L->data)  return ERROR;//说明位置不合理,直接返回 
	DuLinkList temp = L;
	while(j<=i )
	{
		temp = temp->next;
		j++;
	}  
    temp->data = value; //结点
	return OK;
} 

//输出不带头结点链表中的元素 
 void linkedListTraverse(DuLinkList  L)
 {
	DuLinkList p = L;    
 	while(p)
	{    
 	    printf(" %d ",p->data);	
		p = p->next;	    	
	}
 	
}

/*清空链表跟销毁链表的区别 ... 销毁:是先销毁了链表的头,然后接着一个一个的把后面的销毁了,
这样这个链表就不能再使用了,即把包括头的所有节点全部释放。 清空:是先保留了链表的头,
然后把头后面的所有的都销毁,最后把头里指向下一个的指针设为空,
这样就相当与清空了,但这个链表还在,还可以继续使用;即保留了头,后面的全部释放。*/
//销毁链表,包括头结点的所有结点全部释放	 
Status destroy_headlinked_list(DuLinkList & L)	
{
	L->data = 0; 
	DuLinkList p = NULL;
	while(p != L)
	{   
	    p = L->next;
	    free(L);
	    L = p;			    	
	} 
    L = NULL;
 	return OK;
}

/*清空带头结点链表
  1.先保留链表的头结点
  2.然后把头结点后面的所有的都销毁
  3.最后把头结点里指向首元结点的指针设为空 */
Status clear_headlinked_list(DuLinkList  L)	
{
	L->data = 0; 
	DuLinkList p,q;
	p = q = L->next;
	while(p != L)
	{   
	    q = p->next;
	    free(q);
		p = q;			    	
	} 
	L->next = L->prior = L;
 	return OK;
 
}  


//销毁不带头结点的链表 

Status destroy_linked_list(DuLinkList  L)
{
	DuLinkList p;
	while(L)
	{
		p = L->next;
		free(L);
		L = p;
	}
	return OK;
}

int main(void)
{
	DuLinkList L;//头指针L 
	int n = 0;
	printf("请输入所要创建带头结点链表的元素个数:");
	scanf("%d",&n);
	
	printf("\n\n请输入所要创建带头结点链表的元素数据:");
	//if(create_headLinked_List( L, n) )     //头插法 
	if( tail_create_heahLinked_List( L, n) ) //尾插法 
		headlinked_List_Traverse(L);
	int number=0;
	printf("\n请输入查找的元素:");
	scanf("%d",&number);
	if(getDuLNodeIndex(L,number))	
    	printf("%d",getDuLNodeIndex(L, number));
    else printf("未找到! ");
    
    printf("\n该链表的长度为:%d",getLength(L));

    int i=1,value,e;
    printf("\n请输入删除结点的位置:");
	scanf("%d",&i); 
    if(deleteDuLNode( L ,i,e))   
		printf("\n删除结点%d成功\n",e);
    else    
		printf("\n删除结点%d失败\n",e);
    
    headlinked_List_Traverse(L);
    printf("\n该链表的长度为:%d",getLength(L));
     
    printf("\n请输入插入结点的位置和数据:");
    scanf("%d %d",&i,&value); 
    insertDuLNode( L ,i, value);
    printf("\n该链表的长度为:%d",getLength(L));
    headlinked_List_Traverse(L);
    
    printf("\n请输入待修改结点的位置和数据:");
    scanf("%d %d",&i,&value); 	
	updateDuLNode(L,i, value);
	headlinked_List_Traverse(L);
	
	if(destroy_headlinked_list(L))
		printf("\n销毁带头结点链表L成功!\n");
	else printf("销毁失败!!\n"); 

	
	return 0;
} 
  


 

六、总结

 

1.理解双向循环链表的关键就是理清链表的首尾关系,即临界点,另外就是要勤加练习双向思考,多实践

2.每插入或者删除一个结点时,都要考虑前驱后继的关系

3.双向循环链表可以做链表的逆序输出、双向查找等等,有着很多的优点,代价就是多了一个指针域

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值