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

Posted 不倒翁*

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了双链表的实现(C语言)---数据结构相关的知识,希望对你有一定的参考价值。

文章目录


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

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;

以上是关于双链表的实现(C语言)---数据结构的主要内容,如果未能解决你的问题,请参考以下文章

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

一篇解双链表(0基础看)(C语言)《数据结构与算法》

深度解析数组单链表和双链表

C语言中特殊结构类型“双链表”

基础数据结构---双链表go语言的代码实现

基础数据结构---双链表go语言的代码实现