C语言数据结构之线性表-双向链表篇

心有半亩花田

藏于世俗人间


🎥烟雨长虹,孤鹜齐飞的个人主页

🔥个人专栏

🎥前期回顾-单链表

期待小伙伴们的支持与关注!!!


目录

双向链表的介绍 

双向链表的结构 

双向链表的功能 

 为节点分配动态内存空间 

创建双向链表的哨兵位 

打印双向链表 

双向链表的头插 

双向链表的尾插 

双向链表的头删

双向链表的尾删

 双向链表的查找

双向链表的指定位置之后插入节点 

双向链表的删除指定节点 

双向链表的销毁 

代码的整体实现 

总结# 

双向链表的介绍 

双向链表:它的每个数据结点中都有两个指针,分别指向前驱和后驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点后驱结点

双向链表的结构 

双向链表的全称:带头双向循环链表

注意:这里的 带头 跟前⾯我们说的 头节点 是两个概念,实际前面的在单链表阶段称呼不严谨,但是为了大家更好的理解就直接称为单链表的头节点

带头链表里的头节点,实际为 哨兵位 ,哨兵位节点不存储任何有效元素,只是起个带头作用

哨兵位存在的意义: 遍历循环链表避免死循环

双向链表的结构定义:

typedef int ListType;       //将数据类型重命名,防止以后换类型所带来的麻烦

typedef struct ListNode
{
	ListType data;
	struct ListNode* next;  //指向该节点的后驱指针
	struct ListNode* prev;  //指向该节点的前驱指针
}ListNode;                  //结构体类型的重命名,为了以后书写方便

跟我们讲到过的火车模拟差不多,现在是一个车厢存放了两把钥匙,即可以访问该车厢的前车厢也可以访问该车厢的后车厢

今天我将带大家去实现双向链表的 增删查改

双向链表的功能 

 为节点分配动态内存空间 

后面我们要在单链表中进行头插和尾插,为了方便和减少代码的重复度,我们分装一个函数用来专门创建新结点

ListNode* ListMalloc(ListType x)
{
	ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));//利用malloc开辟动态空间
	assert(newNode);                         //判断空间是否开辟成功
	newNode->data = x;                       //读入数据
	newNode->next = newNode->prev = newNode; //该空间的后驱以及前驱指针都指向该节点形成一个环
	return newNode;                          //这个节点空间已经创建好了返回出去
}

创建双向链表的哨兵位 

我们设置了 -1 节点就是我们的带头节点也就是 哨兵位

ListNode* LTInit()
{
	ListNode* head = ListMalloc(-1);
	return head;
}

打印双向链表 

我们写代码最好是写一点测一点,不要最后才调试,所以我们先把打印接口先规划好

void ListPrint(ListNode* head) {
	assert(head);                  //判断链表为不为空
	ListNode* pcur = head->next;   //定义一个指针指向哨兵位的下一个节点,也就是第一个节点
	while (pcur != head)           //因为双向链表是一个循环链表,尾节点的下一个就是哨兵位
	{
		printf("%d->", pcur->data);//打印数据
		pcur = pcur->next;         //访问下一个节点数据
	}
	printf("\n");
}

双向链表的头插 

void ListPushFront(ListNode* head, ListType x)
{
	assert(head);                     //判断链表为不为空
	ListNode* newNode = ListMalloc(x);//开辟动态空间
	newNode->next = head->next;       //新节点的后驱指针指向第一个节点
	newNode->prev = head;             //新节点的前驱指针指向哨兵位
	head->next->prev = newNode;       //第一个节点的前驱指针指向新节点
	head->next = newNode;             //哨兵位的后驱指针指向新节点
}

双向链表的尾插 

void ListPushBack(ListNode* head, ListType x)
{
	assert(head);                     //判断链表是否为空
	ListNode* newNode = ListMalloc(x);//开辟动态空间
	newNode->next = head;             //新节点的后驱指针指向哨兵位
	newNode->prev = head->prev;       //新节点的前驱指针指向尾节点
	head->prev->next = newNode;       //尾节点的后驱指针指向新节点
	head->prev = newNode;             //哨兵位的前驱节点指向新节点
}

双向链表的头删

void ListPopFront(ListNode* head)
{
	assert(head);                //判断链表是否为空
	assert(head->next != head);  //判断哨兵位的下一个节点为不为空
	ListNode* pulic = head->next;//定义一个指针记录要删除首节点的位置
	ListNode* next = pulic->next;//定义一个指针指向首节点的后驱节点
	next->prev = head;           //首节点的后驱节点也就是第二节点的前驱指针指向哨兵位
	head->next = next;           //哨兵位的后驱指针指向第二节点
	free(pulic);                 //释放首节点
	pulic = NULL;
}

双向链表的尾删

void ListPopBack(ListNode* head)
{
	assert(head);                  //判断链表是否为空
	assert(head->next != head);    //判断哨兵位的下一个节点是否为空
	ListNode* pulic = head->prev;  //定义一个指针指向尾节点
	ListNode* prev = pulic->prev;  //定义一个指针指向尾节点的前驱节点
	prev->next = head;             //尾节点的前驱节点的后驱指针指向哨兵位
	head->prev = prev;             //哨兵位的前驱指针指向尾节点的前驱节点
	free(pulic);                   //释放尾节点
	pulic = NULL;
}

 双向链表的查找

ListNode* ListFind(ListNode* head, ListType x)
{
	assert(head);                //判断链表是否为空
	ListNode* pcur = head->next; //定义一个指针指向哨兵位
	while (pcur != head)         //遍历链表寻找指定数据
	{
		if (pcur->data == x)
		{
			return pcur;         //找到了返回当前指针
		}
		pcur = pcur->next;
	}
	return NULL;                 //找不到返回NULL
}

双向链表的指定位置之后插入节点 

void ListInsert(ListNode* pos,ListType x) 
{
	assert(pos);                     //判断pod合不合法
	ListNode* newnode = ListMalloc(x);
	
	newnode->next = pos->next;
	newnode->prev = pos;

	pos->next->prev = newnode;
	pos->next = newnode;
}

双向链表的删除指定节点 

void ListErase(ListNode* pos)
{
	pos->next->prev = pos->prev;//指定元素的后驱节点的前驱指针指向指定节点的前驱节点
	pos->prev->next = pos->next;//指定元素的前驱节点的后驱指针指向指定节点的后驱节点
	free(pos);                  //释放指定元素的空间
	pos = NULL;
}

双向链表的销毁 

void ListDesTroy(ListNode* head) {
	//哨兵位不能为空
	assert(head);

	ListNode* pcur = head->next;
	while (pcur != head)
	{
		ListNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	//链表中只有一个哨兵位
	free(head);
	head = NULL;
}

代码的整体实现 

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int ListType;      
typedef struct ListNode
{
	ListType data;
	struct ListNode* next;  
	struct ListNode* prev;  
}ListNode;             
ListNode* ListMalloc(ListType x)
{
	ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
	assert(newNode);                         
	newNode->data = x;                       
	newNode->next = newNode->prev = newNode; 
	return newNode;                          
}

ListNode* LTInit()
{
	ListNode* head = ListMalloc(-1);
	return head;
}

void ListPrint(ListNode* head) {
	assert(head);                 
	ListNode* pcur = head->next;  
	while (pcur != head)           
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;         
	}
	printf("\n");
}


void ListPushBack(ListNode* head, ListType x)
{
	assert(head);                     
	ListNode* newNode = ListMalloc(x);
	newNode->next = head;            
	newNode->prev = head->prev;      
	head->prev->next = newNode;       
	head->prev = newNode;            
}

void ListPushFront(ListNode* head, ListType x)
{
	assert(head);                    
	ListNode* newNode = ListMalloc(x);
	newNode->next = head->next;       
	newNode->prev = head;            
	head->next->prev = newNode;       
	head->next = newNode;            
}

void ListPopBack(ListNode* head)
{
	assert(head);                  
	assert(head->next != head);   
	ListNode* pulic = head->prev;  
	ListNode* prev = pulic->prev;  
	prev->next = head;             
	head->prev = prev;           
	free(pulic);                  
	pulic = NULL;
}

void ListPopFront(ListNode* head)
{
	assert(head);               
	assert(head->next != head);  
	ListNode* pulic = head->next;
	ListNode* next = pulic->next;
	next->prev = head;         
	head->next = next;           
	free(pulic);               
	pulic = NULL;
}

ListNode* ListFind(ListNode* head, ListType x)
{
	assert(head);                
	ListNode* pcur = head->next; 
	while (pcur != head)        
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

void ListInsert(ListNode* pos,ListType x) 
{
	assert(pos);                     
	ListNode* newnode = ListMalloc(x);
	
	newnode->next = pos->next;
	newnode->prev = pos;

	pos->next->prev = newnode;
	pos->next = newnode;
}

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

void ListDesTroy(ListNode* head) {
	assert(head);

	ListNode* pcur = head->next;
	while (pcur != head)
	{
		ListNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	free(head);
	head = NULL;
}
void CeShi()
{
	ListNode* plist = LTInit();
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	ListPushBack(plist, 5);
	ListPrint(plist);
}

int main()
{
	CeShi();
	system("pause");
	return 0;
}

总结# 

其实双向链表和单向链表相比改变并不是很大,也只是多了一个指向前驱的指针而已,变化最大的也就是添加节点和删除节点,需要将前面的和后面的节点都进行连接

  • 12
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

烟雨长虹,孤鹜齐飞

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

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

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

打赏作者

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

抵扣说明:

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

余额充值