【C语言|数据结构】双向链表

前言

各位小伙伴大家好,即上回的单向链表之后,双向链表来了,他和单向链表的主要区别就是,他有两个指针,同时指向前面一个节点,和后面一个节点,简直是完美,几乎解决的单向链表的大多数难题

1、初步认识双向链表

1.1 定义:

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

1.2 结构

在这里插入图片描述

这是带头的双向循环链表。

1.3 节点的存储
typedef int DLDataType;

typedef struct LinkNode
{
	DLDataType data;
	struct LinkNode* next;
	struct LinkNode* prv;
}LNode;

2、双向链表的接口函数

2.1 链表的节点的动态申请
LTNode* LTFound(DLDataType x)
{
	LTNode* newNode = (LTNode*)malloc(sizeof(LTNode));

	if (newNode == NULL)
	{
		perror("LTInit()::malloc()");
		return;
	}

	newNode->next = newNode->prv = newNode;
	newNode->data = x;
	return newNode;
}
2.2 链表的初始化
// 法一:
LTNode* LTInit()
{
	LTNode* newhead = LTFound(-1);

	return newhead;
}
法二:
void LTInit(LTNode** pphead)
{
	LTNode* newhead = LTFound(-1);
	*phead = newhead;
}
2.3 尾插
void LTPushBack(LTNode* phead, DLDataType x)
{
	assert(phead);
	 
	LTNode* cmp = LTFound(x);

	// phead phead->prv cmp
	cmp->next = phead;
	cmp->prv = phead->prv;

	phead->prv->next = cmp;
	phead->prv = cmp;
}

尾插这个节点不管是插入在头节点的前面还是尾节点后面都是一样的

2.4 头插
//头插
void LTPushFront(LTNode* phead, DLDataType x)
{
	assert(phead);

	LTNode* tmp = LTFound(x);

	// phead tmp phead->next

	tmp->next = phead->next;
	tmp->prv = phead;

	phead->next = tmp;
	phead->next->prv = tmp;
}

头插必须插入在头节点的后面才是头插,头节点就是哨兵卫,链表进行操作的时候不能操作哨兵卫,否则就不带头了。

2.5 头删
//头删
void LTPopFront(LTNode* phead)
{
	assert(phead && phead->next != phead);
	
	LTNode* cur = phead->next;
	// phead  cur cur->next

	phead->next = cur->next;
	cur->next = phead;

	free(cur);
	cur = NULL;
}
2.5 尾删
//尾删
void LTPopBack(LTNode* phead)
{
	assert(phead && phead->next != phead);

	LTNode* cur = phead->prv;
	
	// phead cur->prv cur
	phead->prv = cur->prv;
	cur->prv->next = phead;

	free(cur);
	cur = NULL;
}
2.6 在pos节点后面添加数据
//在pos位置之后插入数据
void LTInsert(LTNode* pos, DLDataType x)
{
	assert(pos);

	LTNode* cur = LTCreate(x);

	// pos cur pos->next
	cur->next = pos->next;
	cur->prv = pos;

	pos->next = cur;
	pos->next->prv = cur;

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

	// pos->prv pos pos->next

	pos->prv->next = pos->next;
	pos->next->prv = pos->prv;

	free(pos);
	pos = NULL;
}

3、双向链表的实现:

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

typedef int DLDataType;

typedef struct LinkNode
{
	DLDataType data;
	struct LinkNode* next;
	struct LinkNode* prv;
}LTNode;

//双链表的节点创建
LTNode* LTCreate(DLDataType x)
{
	LTNode* newNode = (LTNode*)malloc(sizeof(LTNode));

	if (newNode == NULL)
	{
		perror("LTInit()::malloc()");
		exit(-1);
	}

	newNode->next = newNode;
	newNode->prv = newNode;
	newNode->data = x;
	return newNode;
}

//初始化
//void LTInit(LTNode** pphead)
//{
//	LTNode* newhead = LTFound(-1);
//}

LTNode* LTInit()
{
	LTNode* newhead = LTCreate(-1);

	return newhead;
}

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

	LTNode* cur = phead->next;

	while (cur != phead)
	{
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}

	free(phead);
	phead = NULL;
}

//链表的打印
void LTPrint(LTNode* phead)
{
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

//查找pos节点
LTNode* LTFind(LTNode* phead, DLDataType x)
{
	assert(phead);

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

//插入数据之前,链表必须初始化到只有一个头结点的情况
//不改变哨兵位的地址,因此传一级即可
//尾插
void LTPushBack(LTNode* phead, DLDataType x)
{
	assert(phead);

	LTNode* cmp = LTCreate(x);

	// phead phead->prv cmp
	cmp->next = phead;
	cmp->prv = phead->prv;

	phead->prv->next = cmp;
	phead->prv = cmp;
}

//头插
void LTPushFront(LTNode* phead, DLDataType x)
{
	assert(phead);

	LTNode* tmp = LTCreate(x);

	// phead tmp phead->next

	tmp->next = phead->next;
	tmp->prv = phead;

	phead->next = tmp;
	phead->next->prv = tmp;
}


//尾删
void LTPopBack(LTNode* phead)
{
	assert(phead && phead->next != phead);

	LTNode* cur = phead->prv;

	// phead cur->prv cur
	phead->prv = cur->prv;
	cur->prv->next = phead;

	free(cur);
	cur = NULL;
}

//头删
void LTPopFront(LTNode* phead)
{
	assert(phead && phead->next != phead);

	LTNode* cur = phead->next;
	// phead  cur cur->next

	phead->next = cur->next;
	cur->next = phead;

	free(cur);
	cur = NULL;
}

//在pos位置之后插入数据
void LTInsert(LTNode* pos, DLDataType x)
{
	assert(pos);

	LTNode* cur = LTCreate(x);

	// pos cur pos->next
	cur->next = pos->next;
	cur->prv = pos;

	pos->next = cur;
	pos->next->prv = cur;

}

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

	// pos->prv pos pos->next

	pos->prv->next = pos->next;
	pos->next->prv = pos->prv;

	free(pos);
	pos = NULL;
}


void DLinkListText()
{
	LTNode* phead = LTInit();

	LTPushBack(phead, 1);
	LTPushBack(phead, 2);
	LTPushBack(phead, 3);
	LTPrint(phead);

	LTPushFront(phead, 0);
	LTPushFront(phead, 999);
	LTPrint(phead);

	// LTPopFront(phead);
	// LTPrint(phead);

	LTPopBack(phead);
	LTPrint(phead);

	LTDesTroy(phead);
}
	



int main()
{
	DLinkListText();
	return 0;
}
  • 15
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小石头 10086

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

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

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

打赏作者

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

抵扣说明:

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

余额充值