双链表的实现(C语言)---数据结构

本文详细介绍了双链表的结构体定义、节点的创建与初始化,以及链表的插入(尾插、头插和任意位置插入)、删除(尾删、头删和任意位置删除)、修改(查找与修改数据)和清除(清理所有节点,包括或不包括头结点)等操作。通过示例代码展示了具体实现过程,便于理解和应用。
摘要由CSDN通过智能技术生成


链表可以说是数据结构中最基础的一部分,它分为单链表和双链表,他们的区别在于,单链表的结构中只有一个节点来指向它的后面的元素,而双链表中有两个节点,一个节点可以指向它前面的元素,一个可以指向它后面的元素,这就使得链表的使用更加方便和灵活。下面我们来看看双链表具体是怎么实现的吧。

1.定义链表的结构体

首先,我们可以来定义一个链表的结构体,里面包含三个部分,一个是数据块部分,一个是指向后一个节点的结构体指针,还有一个是指向前一个节点的结构体指针。

typedef struct ListNode
{
	int date;  //数据
	struct ListNode* next; //指向后面的节点的指针
	struct ListNode* prve; //指向前面的节点的指针
}ListNode; 

在实现链表的功能之前,我们先来做一些准备工作。
1.首先是创建一个新的链表节点

ListNode* creatNode(int x)
{
	ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
	newNode->date = x;
	newNode->next = NULL;
	newNode->prve = NULL;
	return newNode;
}

2.然后是链表的初始化,就是创建一个链表的虚拟头结点,方便我们对链表进行增删改查操作。

ListNode* InitList()
{
	ListNode* head = BuyListNode(0);
	head->next = NULL;
	head->prve = NULL;
	return head;
}

2.链表的插入

2.1 链表的尾插

首先我们来看一下双链表的结构,双链表结构为一个环状结构如下图所示
在这里插入图片描述
那么如果我们有一个新的节点newnode那我们应该怎么进行尾插呢?
第一步:既然我么要尾插,那么我们当然是要先找出链表的尾了。在单链表中,我们要找到一个链表的尾,都是遍历链表,找到最后一个节点,但从双链表的结构中我们不难看出,链表的尾就是phead—>prve所指向的节点。
第二步:然后让尾的next指向新节点,新节点的preve指向尾节点.
第三步:然后让phead的prve 指向的新节点,然后新节点的next指向phead。
在这里插入图片描述
下面看代码的具体实现过程

void ListPushBack(ListNode* phead,int x)
{
	assert(phead); 
	ListNode* tail = phead->prve;  //找到尾节点
	ListNode* newNode = BuyListNode(x); //创建新节点
	
	tail->next = newNode; //尾节点的next指向新节点,
	newNode->prve = tail; //新节点的preve指向尾节点.

	newNode->next = phead; //新节点的next指向phead
	phead->prve = newNode; //phead的prve 指向的新节点

}

2.2链表的头插

理解了链表的尾插后,链表的头插也就很好理解了。但需要注意的是,这里的头插不是在phead的前面插入,这里的phead是我们创建出来的虚拟的头结点,方便我们对链表操作的,真实的头结点是p1,如下图所示:

在这里插入图片描述
下面直接看代码

void ListPushFront(ListNode* phead, int x)
{
	ListNode* first = phead->next; //创建一个first指向链表的第一个节点
	ListNode* newNode = BuyListNode(x); //创建一个新节点
	
	phead->next = newNode;  //让phead的next指向新节点
	newNode->prve = phead; //新节点的prve指向
	
	newNode->next = first; //新节点的next指向第一个节点
	first->prve = newNode; //第一个节点的prve指向新节点
}

2.3任意位置插入

在任意位置插入,首先我们要给定一个pos位置,然后自己设定在pos位置前插还是后插,这里我就以前插举例,下面看代码的具体实现
在这里插入图片描述

void ListInsert(ListNode* pos, int x)
{
	assert(pos);
	ListNode* newNode = BuyListNode(x);//创建出新结点
	ListNode* posPrve = pos->prve; //记录pos位置的前一个结点
	
	newNode->next = pos; //新节点的next指向pos;
	pos->prve = newNode; //pos的prve 指向newNode

	posPrve->next = newNode;  //pos的前一个节点指向新节点
	newNode->prve = posPrve; //新节点的前一个节点指向pos的前一个节点
	
}

3.链表节点的删除

3.1链表的尾删

链表的尾删同样是,第一步:先找到链表的尾,也就是之前分析的phead->prve。
第二步:然后就是要找到链表尾的前一个节点,也就是phead->prve->prve;
第三步:然后让尾的前一个节点的next指向phead;让phead的prve指向尾的前一个节点。
下面看代码具体实现部分:

void ListPopBack(ListNode* phead)
{
	assert(phead); 
	assert(phead->next!=phead); //只剩phead一个节点时不能删除
	ListNode* tail = phead->prve;  //找到尾节点
	ListNode* tailPrve = tail->prve; //找到尾节点的前一个节点

	tailPrve->next = phead; //尾的前一个节点的next指向phead
	phead->prve = tailPrve; //让phead的prve指向尾的前一个节点
	free(tail); //释放掉尾节点的空间
	tail = NULL;
}

3.2链表的头删

链表的头删和尾删的思路也就差不多,就是要找到链表的头,以及链表头的下一个节点,来方便进行删除操作。话不多说,直接来看代码

void ListPopFront(ListNode* phead)
{
	assert(phead);
	assert(phead != phead->next);
	
	ListNode* first = phead->next; //找到头结点
	ListNode* second = first->next; //找到头结点的下一个结点

	phead->next = second; //phead的next指向头结点的下一个结点
	second->prve = phead;//头结点的下一个节点指向phead
	
	free(first); //释放掉头结点多占的空间
	first = NULL:
	
}

3.3任意位置的删除

任意位置的删除实现起来比较简单,也是只要知道该位置的前一个节点和后一个节点就行了

void ListErase(ListNode* pos)
{
	ListNode* posPrve = pos->prve;
	ListNode* posNext = pos->next;
	
	posPrve->next = posNext;
	posNext->prve = posPrve;
	free(pos);
	pos=NULL;
}

4.链表的修改

4.1链表的查找

要修改链表中某个结点的值,那么我们必须先要找到这个节点,然后对其中的值进行修改,这个比较简单,直接看代码吧。

ListNode* ListFind(ListNode* phead, int x)
{
	assert(phead);
	ListNode* cur = phead->next;
	while(cur)
	{
		if(cur->date == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

4.2链表数据的修改

找到这个节点后,然后进行修改

void ListModify(ListNode* phead, int x , int y)
{
	assert(phead);
	ListNode* pos = ListFind(phead, x);
	if(pos!=NULL)
	{
		pos->date = y;
	}
	else
	{
		printf("链表中无此节点\n");
    }
}

5.清除链表

清除链表,这里提供两种方式,一种是清除链表中所有的节点,包括头结点,另一种是清除链表中所有的节点,不包括头结点

5.1清理链表中的所有节点,不包括头结点

将链表中所有节点删除,但保留头结点

void ListClear(ListNode* phead)
{
	ListNode* cur  = phead->next;
	while(cur)
	{
		ListNode* next = cur->next;
		free(cur);
		cur = next;
	}
	phead->next = phead; //让头结点指向自己
	phead->prve = phead;
}

5.2清理链表中的所有节点,包括头结点

void ListDestory(ListNode** phead)
{
	assert(*phead);
	ListClear(*phead);  //调用上面的函数
	free(*phead);
	*phead = NULL;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不倒翁*

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

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

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

打赏作者

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

抵扣说明:

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

余额充值