双向链表的实现

本文详细介绍了双向链表的概念,包括其组成部分、哨兵位的作用,以及双向链表的初始化、打印、节点操作(如头插、尾插、删除和查找)和销毁等关键功能的实现方法。
摘要由CSDN通过智能技术生成

一.双向链表相关知识的介绍

1.双向链表又叫带头双向循环链表

注:1.只有带有哨兵位的链表才叫带头链表,前面所介绍的单链表是不带头单向不循环链表

        2.哨兵位只起到不让链表为空链表的作用,它里面不存储有效数据,但是它不能被删除,节              点地址也不能被修改

2.由于双向链表是循环的,所以双向链表包含三部分:<1>指向前一个结点的指针

                                                                                      <2>该节点存储的数据

                                                                                      <3>指向下一个节点的指针

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

3.双向链表为空表示链表中只剩下哨兵位一个节点

但单链表为空表示链表中不存在节点

二.双向链表的实现

1.双向链表的初始化

1>双向链表包含哨兵位,故初始化双向链表即为其创建一个哨兵位

2>由于哨兵位不存储有效数据,故而在开辟空间时传个小于0的值即可

3>代码实现

void LTInit(LTNode** pphead)
{
	//给双向链表创建一个哨兵位
	*pphead = LTBuyNode(-1);
}

2.打印双向链表

1>当pcur=phead时,说明已经循环遍历一次链表了,此时应跳出循环

2>代码实现

void LTPrint(LTNode* phead)
{
	assert(phead);
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("\n");
}

3.申请新节点

1>双向链表的打印应该从第一个有效数据开始打印,故而pcur=phead->next,指向哨兵位后的一       个节点

2>由于双向链表是循环的,所以应该让prev,next均指向newnode的位置

3>代码实现

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

4.尾插

1>尾插应该在哨兵位前面一个节点的后面插入

2>此时该改变的数据有phead->prev,phead->prev->next,node->prev,node->next

3>为了防止不慎改变原有的数据导致出错,我们可以先对node的相关指针进行赋值

    node->prev = phead->prev;//让新节点的prev指向原来的“尾节点”
    node->next = phead;//让新节点的prev指向哨兵位
    phead->prev->next = node;//让原先“尾节点”的next指向新节点
    phead->prev = node;//让node成为新的“尾节点”

4>代码实现

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

5.头插

1>头插是插在哨兵位后一个节点的前面

2>还是先对node的指针进行赋值

    node->prev = phead;
    node->next = phead->next;
    phead->next = node;
    phead->next->prev = node;

3>代码实现

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

6.尾删

1>链表不能为空且链表为有效链表

2>代码实现

void LTPopBack(LTNode* phead)
{
	//链表有效且不为空(即不能只有一个哨兵位)
	assert(phead && phead->next!=phead);
	LTNode* del = phead->prev;
	del->prev->next = phead;
	phead->prev = del->prev;
	free(del);
	del = NULL;
}

7.头删

1>与尾删前提相同

2>代码实现

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

8.在指定位置后插入节点

1>代码实现

void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);
	LTNode* node = LTBuyNode(x);
	node->prev = pos;
	node->next = pos->next;
	pos->next = node;
	pos->next->prev = node;
}

9.删除指定位置处的节点

1>代码实现

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

10.查找数据

1>代码实现

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;
}

11.销毁双向链表

1>定义一个指针next记录pcur后的节点,保证销毁下一个节点时可以找到它

2>代码实现

void LTDestory(LTNode* phead)
{
	assert(phead);
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		LTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	//此时pcur指向phead,phead还未被销毁
	free(phead);
	phead = NULL;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值