【C语言】双向循环链表

本文详细介绍了双向循环链表的概念及其优势,包括如何创建头节点、创建新节点、表尾插入、表头插入、打印链表、查找指定值、在特定位置插入数据以及删除指定值的操作。通过示例代码展示了每个操作的具体步骤,强调了在操作过程中需要注意的顺序。双向循环链表虽然比单链表复杂,但提供了便利的前后节点访问,有利于提高算法效率。
摘要由CSDN通过智能技术生成

引言

双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。

双向循环链表

双向循环链表节点

typedef int CycleList_Data;

typedef struct Cycle_Node
{
	CycleList_Data data;
	struct Cycle_Node* front;
	struct Cycle_Node* next;
}Cycle_List;

双向循环链表和单链表相比增加了一个指向前驱的指针域,可以通过某节点的指针p即可以直接得到他的前驱和后继,这样在有些操作中需要找前驱时,无需再从头寻找。
假设pMove指向双链表的某一节点,即pMove是该节点的指针。
注意:

1)pMove->front->next表示的是pMove节点的前驱节点的后继节点的指针,即与pMove相等。
2)pMove->next->front表示的是pMove节点的后继节点的前驱节点的指针,也与pMove相等

创建头节点

Cycle_List* Create_HeadNode()
{
	Cycle_List* Head_Node = (Cycle_List *)malloc(sizeof(Cycle_List));
	
	if(Head_Node == NULL)
	{
	//	printf("创建失败\n");
		return NULL;
	}
	else
	{
		Head_Node->front = Head_Node;
		Head_Node->next = Head_Node;

		return Head_Node;
	}
}

当链表中只有一个头节点时,要清楚指针的指向,前驱指针域和后继指针域指向本身

在这里插入图片描述

创建节点

Cycle_List* Create_NewNode(CycleList_Data data)
{
	Cycle_List* New_Node = (Cycle_List*)malloc(sizeof(Cycle_List));
	
	if(New_Node == NULL)
	{
		//printf("创建失败\n");
		return NULL;
	}
	New_Node->data = data;
	New_Node->front = NULL;
	New_Node->next = NULL;

	return New_Node;
}

表尾插入

void Insert_List(Cycle_List* Head_Node,CycleList_Data data)
{
	Cycle_List* New_Node = Create_NewNode(data);
	
	New_Node->next = Head_Node;
	New_Node->front = Head_Node->front;
	
	Head_Node->front->next = New_Node;
    Head_Node->front = New_Node;
}

双向循环链表有表尾插入和表头插入,在这里使用的是表尾插入。
记住,在插入过程中顺序不可搞错
在这里插入图片描述

1、New_Node->next = Head_Node;
2、New_Node->front = Head_Node->front;
3、Head_Node->front->next = New_Node;
4、Head_Node->front = New_Node;

表头插入
在这里插入图片描述
表头插入思路和表尾类似

打印链表*

void Printf_List(Cycle_List* Head_Node)
{
	Cycle_List* pMove = Head_Node->next;
	
	while(pMove != Head_Node)
	{
		printf("%d->",pMove->data);
		pMove = pMove->next;
	}
	printf("NULL\n");
}

定义一个移动指针pMove=Head_Node->next开始遍历整个链表,当pMove=Head_Node时,链表遍历一遍
在这里插入图片描述
查找某值

void Find_Bydata_List(Cycle_List* Head_Node,CycleList_Data posdata)
{
    Cycle_List* posNode = Head_Node->next;
    
    while(posNode->data != posdata)
    {
        if(posNode == Head_Node)
        {
            printf("查找失败\n");
            return ;
        }
        posNode = posNode->next;
    }
    printf("查找成功\n");
    return ;
}

插入数据

void Insert_Bydata_List(Cycle_List* Head_Node,CycleList_Data posdata,CycleList_Data data)
{
    Cycle_List* posNode = Head_Node->next;
    Cycle_List* New_Node = Create_NewNode(data);
    
    while(posNode->data != posdata)
    {
        if(posNode == Head_Node)
        {
            printf("插入失败\n");
            return ;
        }
        posNode = posNode->next;
    }
    New_Node->next = posNode;
    posNode->front->next = New_Node;
    
    New_Node->front = posNode->front;
    posNode->front = New_Node;
}

插入数据首先找到插入到哪个位置,然后进行插入操作。
先看下面的图片
在这里插入图片描述
找到“位置”posNode,然后依次进行①②③④操作。
上述操作为指定位置前驱插入

后继插入

void InsterData_Cycle_List(Cycle_List* Head_Node,CycleList_Data posdata,CycleList_Data data)
{
	Cycle_List* posNode = Head_Node->next;
	Cycle_List* New_Node = Create_NewNode(data);

	while(posNode->data != posdata)
	{
		if(posNode == Head_Node)
		{
			printf("插入失败\n");
			return ;
		}
		posNode = posNode->next;
	}
	
	New_Node->next = posNode->next;
	posNode->next = New_Node;

	New_Node->front = posNode;
	New_Node->next->front = New_Node;
    
	printf("插入成功\n");
	return ;
}

分析方法和前驱一样,这里不过多叙述。

删除某值

void Delete_Cycle_List(Cycle_List* Head_Node,CycleList_Data posData)
{
	Cycle_List* posNode = Head_Node->next;
	
	while(posNode->data != posData)
	{
		if(posNode == Head_Node)
		{
			printf("不存在该值,删除失败\n");
			return ;
		}
		posNode = posNode->next;
	}
	posNode->front->next = posNode->next;
	posNode->next->front = posNode->front;
	
	free(posNode);

	printf("删除成功\n");
}

在这里插入图片描述
看上面的图片,删除节点是不是和插入节点很类似?
删除节点时直接执行图中的1、2步骤就行了,然后再释放一下posNode节点所申请的内存即可。

①、posNode->front->next = posNode->next;
②、posNode->next->front = posNode->front;

简单总结一下,双向链表相对于单链表来说,要更复杂一些,毕竟它多了front指针,对于插入和删除时,需要格外小心。另外它由于每个结点都需要记录两份指针,所以在空间上是要占用略多一些的。不过,由于它良好的对称性,使得对某个结点的前后结点的操作,带来了方便,可以有效提高算法的时间性能。说白了,就是用空间来换时间

在插入节点时,要注意一下执行顺序,我们以插入某值来分析一下:
在这里插入图片描述
一定要记住第4步posNode->front = New_Node不能放到第一步。
假设先执行了第4步,此时posNode的前驱变成了New_Node,而原先链表posNode前面的节点我们就丢失了,我们也就插入失败了.
我测试了几遍发现,1、2、3步骤不固定,看个人爱好。

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

点灯大师~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值