数据结构链表下——双链表

双向链表

双向链表具体为带头循环双向链表
为保持接口的一致性,这里我们对具体的代码进行了优化,都是用一级指针。


1.双向链表的定义

定义一个双向链表的每一个结点又三个部分组成,分为该结点所存储的数据data,该结点的next指针指向该节点的下一个结点,该结点的prev指针指向该节点的前一个结点,这里我们定义为LTNode

typedef int LTDataType;
typedef struct ListNode
{
	LTDataType data;
	struct ListNode* next;
	struct ListNode* prev;
}LTNode;

2.双向链表的初始化

在初始化之前我们先要为链表申请空间,头结点的prev指针和next指针都指向自己。

LTNode* LTBuyNode(LTDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	newnode->data = x;
	newnode->next = newnode->prev = newnode;
	return newnode;
}

初始化头结点
初始其数据为-1即可

LTNode* LTInit()
{ 
	LTNode * phead = LTBuyNode(-1);
	return phead;
}

3.头部插入

双向链表的头部插入与单链表的头部插入有所不同,双向链表的头部插入是在头结点的后一个位置插入,如果在头结点的前面插入,我们会发现和尾部插入相同,所以是在头结点的后方插入。
创建一个结点值为x
让新结点的next指针指向头结点的next结点
新结点的prev指针指向头结点
头结点的next指针指向的结点的prev指针指向新结点
头结点的next指针指向新结点
即完成头部插入。

void LTPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = LTBuyNode(x);
	newnode->next = phead->next;
	newnode->prev = phead;
	phead->next->prev = newnode;
	phead->next = newnode;
}

4.尾部插入

首先申请一个结点赋值为x
新结点的next指针指向头结点
因为头结点的prev指针所指向的就是最后一个数据,新结点的prev指针指向头结点的prev指针所指向的位置
原尾结点的next指针指向新结点
头结点的prev指针指向新结点

void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = LTBuyNode(x);
	newnode->next = phead;
	newnode->prev = phead->prev;
	phead->prev->next = newnode;
	phead->prev = newnode;
}

5.判空

因为判空我们需要用到多次,我们直接封装成函数,用的时候直接调用即可。

bool LTEmpty(LTNode* phead)
{
	assert(phead);
	return phead->next == phead;
}

6.尾部删除

首先要对链表进行判空,若为空,删除自然是不可以执行的
申请两个指针,del和prev
del指针指向头结点prev结点
prev指针执行那个del的头结点,也就是头结点的prev指针所指向的prev指针所指向的数据(头结点的前两个)
让prev指针的next的指针指向头结点
头结点的prev指针指向prev结点
最后不要忘记将del指针释放并置空

void LTPopBack(LTNode* phead)
{
	assert(phead);
	assert(!LTEmpty(phead));
	LTNode* del = phead->prev;
	LTNode* prev = del->prev;
	prev->next = phead;
	phead->prev = prev;
	free(del);
	del = NULL;
}

7.头部删除

头部删除也同理,并不是删除头结点,而是删除头结点的下一个结点
创建一个指针del指向头结点的next结点
del的next指针的prev指针指向头结点
头结点的next指针del的next指针所指向的位置

void LTPopFront(LTNode* phead)
{
	assert(phead);
	assert(!LTEmpty(phead));
	LTNode* del = phead->next;
	del->next->prev = phead;
	phead->next = del->next;
	free(del);
	del = NULL;
}

8.查找数据

在链表操作中,我们不能改变头指针,一旦更改头指针后,我们将找不到链表的头,我们创建一个指针pcur指向头结点的下一个结点
然后将pcur进行遍历,若找到了就return返回
找不到就返回空

LTNode* LTFind(LTNode* phead,LTDataType x)
{
	assert(phead);
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		if (pcur ->data==x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

9.在指定位置之后插入数据

此操作比较简单,不再赘述

void LTInset(LTNode* pos, LTDataType x)
{
	assert(pos);
	LTNode* newnode = LTBuyNode(x); 
	newnode->next = pos->next;
	newnode->prev = pos;
	pos->next->prev = newnode;
	pos->next = newnode;

}

10.删除指定位置的结点

void LTErase(LTNode* pos)
{
	assert(pos);
	pos->prev->next = pos->next;
	pos->next->prev = pos->prev;
	free(pos);
	pos = NULL;
}

11.链表的销毁

先将头结点除外的所有结点释放,置空,最后对头结点也同样操作
在销毁的时候我们需要注意,因为我们想要保持接口的一致性,从而都用的一级指针,在链表的销毁的时候我们需要对其手动置零。

void LTDesTroy(LTNode* phead)
{
	assert(phead);
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		LTNode* Next = pcur->next;
		free(pcur);
		pcur = NULL;

	}
	free(phead);
	phead = pcur = NULL;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值