双链表详解(后附完整源代码)

一.基本框架

在这里插入图片描述

首先创立三个源文件分模块编写。

在这里插入图片描述

初始化定义一些结构。

在这里插入图片描述

定义新节点,每次需要一个新节点时就从该函数调用。

二.初始化哨兵位

在这里插入图片描述

在这里插入图片描述

这里设立哨兵位的好处就是可以快速找尾并且不用判断链表是否为空(在单链表中我们需要判断,如果链表为空就给头节点赋值,但在这因为存在哨兵位故链表不可能为空)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

三.尾插

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

四.打印链表

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

五.尾删

在删除之前需要判断一下链表是否为空,所以增加一个函数。

在这里插入图片描述

接着进行尾删

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

六.头插和头删

在这里插入图片描述

在这里插入图片描述

七.任意位置插入和删除

在此之前需要找到该节点

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

开始插入和删除

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

八.销毁

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

这里链表就完成了,其实可以看出后面我们写的任意位置插入和删除,那么头插头删尾插尾删,其实是可以直接用这两个函数替代的,具体怎么替代就不再写啦,原理很简单,大家可以试试看。

九.源代码

test.c

#include<stdio.h>
#include"List.h"

//测试链表
void Test()
{
	LTNode* plist = LTInit();//初始化哨兵位
	
	LTPushBack(plist, 1);//插入一个值为1的节点
	LTPushBack(plist, 2);//插入一个值为2的节点
	LTPushBack(plist, 3);//插入一个值为3的节点
	LTPushBack(plist, 4);//插入一个值为4的节点

	LTPopBack(plist);//删除最后一个

	LTPushFront(plist, 5);//头插5
	LTPushFront(plist, 4);//头插4
	LTPushFront(plist, 3);//头插3

	LTPopFront(plist);//头删
	
	LTNode*pos=TLFind(plist,5);//找到值为5的节点

	LTInsert(pos,6);//在pos前面位置插入6
	LTErase(pos);//删除pos节点

	LTPrint(plist);//打印链表

	Dstroy(plist);//销毁链表

}

int main()
{
	Test();
	return 0;
}

list.h

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
#define LTDataType int

typedef struct ListNode
{
	struct ListNode* pre;
	struct ListNode* next;
	LTDataType data;//这里依然设定特定结构为了防止以后储存不同类型数据需要不断更改类型
}LTNode;

LTNode* LTInit();//初始化哨兵位

void LTPushBack(LTNode* phead, LTDataType x);//尾插

void LTPrint(LTNode*phead);//打印链表

void LTPopBack(LTNode* phead);//尾删

void LTPushFront(LTNode* phead, LTDataType x);//头插

void LTPopFront(LTNode* phead);//头删

LTNode* TLFind(LTNode* pos, LTDataType x);//找到改节点

void LTInsert(LTNode* pos, LTDataType x);//在第pos个节点前插入

void LTErase(LTNode* pos);//删除pos节点

void Dstroy(LTNode* phead);//销毁链表

list.c

#include"List.h"


LTNode* BuyList(LTDataType x)
{
	LTNode* node = (LTNode*)malloc(sizeof (LTNode));//开辟一个新节点
	if (node == NULL)
	{
		perror("malloc fail");
		return NULL;
	}//防止开辟失败
	node->next = NULL;
	node->pre = NULL;
	node->data = x;//初始化节点

	return node;
}//开辟节点


LTNode* LTInit()//初始化
{
	LTNode*phead = BuyList(-1);//初始化值为-1
	phead->next = phead;
	phead->pre = phead;//初始化两个指针都指向自己
	return phead;
}


bool LTEmpty(LTNode* phead)//判断是否为空
{
	assert(phead);
	
	return phead->next == phead;
}



void LTPushBack(LTNode* phead, LTDataType x)//尾插
{
	assert(phead);//断言头节点是否为空

	LTNode* newnode = BuyList(x);//创建一个值为x的节点
	LTNode*tail = phead->pre;//让尾部指向之前最后一个节点

	tail->next = newnode;//之前最后一个节点指向现在最后一个节点
	newnode->next = phead;//新节点的下一个节点指向哨兵位
	newnode->pre = tail;//新节点的前一个指向之前最后一个节点
	phead->pre = newnode;//哨兵位的前一个节点指向新节点

}


void LTPrint(LTNode*phead)//打印链表
{
	assert(phead);

	LTNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d<=>", cur->data);
		cur = cur->next;
	}
	printf("\n");
}


void LTPopBack(LTNode* phead)//尾删
{
	assert(phead);
	assert(!LTEmpty(phead));//断言链表是否为空

	LTNode* tail = phead->pre;//找到尾
	LTNode* tailPre = tail->pre;//找到尾部前一个

	phead->pre = tailPre;//让哨兵位前一个指向倒数第二个节点
	tailPre->next = phead;//倒数第二个指向哨兵位
	free(tail);//释放尾节点
	tail = NULL;
}



void LTPushFront(LTNode* phead, LTDataType x)//头插
{
	LTNode* newnode = BuyList(x);//创建一个值为x的节点

	newnode->next = phead->next;
	newnode->pre = phead;//改变新节点的两个指针
	phead->next->pre = newnode;//改变原第一个节点的前一个指针
	phead->next = newnode;//改变哨兵位的后一个指针

}

void LTPopFront(LTNode* phead)//头删
{
	assert(phead);
	assert(!LTEmpty(phead));//判断释放为空

	LTNode* tail = phead->next;
	LTNode* tailP = tail->next;

	phead->next = tailP;
	tailP->pre = phead;
	free(tail);
	tail = NULL;
}

LTNode* TLFind(LTNode* pos, LTDataType x)//找到改节点
{
	assert(pos);

	LTNode* cur = pos->next;
	while (cur!= NULL)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}

	return NULL;
}


void LTInsert(LTNode* pos, LTDataType x)//在第pos个节点前插入
{
	assert(pos);

	LTNode* newnode = BuyList(x);
	newnode->next = pos;
	newnode->pre = pos->pre;//改变新节点的两个指针
	pos->pre->next = newnode;//改变原pos前节点的后指针
	pos->pre = newnode;//改变Pos的前指针

}

void LTErase(LTNode* pos)//删除pos节点
{
	assert(pos);

	LTNode* p = pos->pre;
	LTNode* n = pos->next;

	p->next = n;
	n->pre = p;
	free(pos);
}


void Dstroy(LTNode* phead)//销毁链表
{
	assert(phead);

	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* n = cur->next;
		free(cur);
		cur = n;
	}
	free(phead);
}
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

咸蛋挞

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

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

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

打赏作者

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

抵扣说明:

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

余额充值