【C++】数据结构 双链表的实现(企业存储用户数据的实现)

本篇博客给大家带来的是用C++语言来实现数据结构的双向链表(企业存储用户数据的实现)

🐟🐟文章专栏:C++

🚀🚀若有问题评论区下讨论,我会及时回答

❤❤欢迎大家点赞、收藏、分享

先给大家说一声抱歉,之前被学校安排去上海做服务员了,没有更新,请大家多多包涵!

今日思想:曙光之前尽是黑暗!

一、双向链表的性质

双向链表是线性的,逻辑结构:是线性的,物理结构:不一定线性。

二、双向链表的定义 ​​​​​

图一:

注意:这里的head结点是不存储数据的,由上图所示我们可知双向链表通过存储前后结点的地址来实现循环,可见双向链表是可以往前或者往后遍历的。

三、双向链表的实现

实现方法:双向链表的初始化、尾插、头插、尾删、头删、在pos(指定位置)之后插入数据、在pos位置、删除pos位置的结点、双向链表的打印。

1、双向链表的初始化

如图一所示我们我们先创造一个结构体,这个结构体包含前一个结点的地址、存储的数据、下一个结点的地址。

代码实例:

cpp 复制代码
//List.h文件
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//定义双向链表的结构
typedef int LTDataType;
typedef struct ListNode
{
	LTDataType data;//存储的数据
	struct ListNode* next;//下一个结点
	struct ListNode* prev;//上一个结点
}LTNode;

我们要进行初始化,头结点是不存储任何数据的,那么接下来的头插和尾插等要申请结点来存储数据,所以可以利用这一点我们对头结点进行初始化。

代码实例:

cpp 复制代码
//List.h文件
//双向链表的初始化
LTNode* LTInit();
cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#include"List.h"
//List.c文件
//申请结点
LTNode* buyNode(LTDataType x)
{
	LTNode* node = (LTNode*)malloc(sizeof(LTNode));
	if (node == NULL)
	{
		perror("malloc fail!");
	}
	node->data = x;
	node->next = node->prev = node;
	return node;
}

//初始化
LTNode* LTInit()
{
	LTNode* phead = buyNode(-1);
	return phead;
}

2、双向链表的尾插

图二:

和图一不同的是我们在这个双向链表的尾部插入一个结点,那么受影响的有新结点的前后地址和node3的下一个地址、head的前地址的指向。

代码实例:

cpp 复制代码
//List.h文件
//尾插
void LTPushBack(LTNode* phead, LTDataType x);
cpp 复制代码
//List.c文件
//尾插
//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = buyNode(x);
	newnode->prev = phead->prev;
	newnode->next = phead;
	phead->prev->next = newnode;
	phead->prev = newnode;
}

3、双向链表的头插

图三:

头插不能在head之前插入,因为这样和尾插没什么区别,所以我们在head的后面插入,那么受影响只有新结点,改变前后地址的指向就行。

代码实例:

cpp 复制代码
//List.h文件
//头插
void LTPushFront(LTNode* phead, LTDataType x);
cpp 复制代码
//List.c文件
//头插
void LTPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = buyNode(x);
	newnode->prev = phead;
	newnode->next = phead->next;

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

4、双向链表的尾删

图四:

如图四,我们把node3结点删除那么受影响的有head的前一个指向和node2结点的下一个指向。

代码实例:

cpp 复制代码
//List.h文件
//尾删
void LTPopBack(LTNode* phead);
cpp 复制代码
//List.c文件
bool LTEmpty(LTNode* phead)
{
	assert(phead);
	return phead->next == phead;
}
//尾删
void LTPopBack(LTNode* phead)
{
	assert(!LTEmpty(phead));
	LTNode* del = phead->prev;//先把head结点上一个结点的地址存储起来也就是微博的地址,以便后面释放
	del->prev->next = phead;//改变倒数第二个结点的下一个存储的地址
	phead->prev = del->prev;

	free(del);
	del = NULL;
}

5、双向链表的头删

如图五:

如图五我们把node1结点删除,那么受影响的有head结点的下一个指向和node2的上一个指向。

代码实例:

cpp 复制代码
//List.h文件
//头删
void LTPopFront(LTNode* phead);
cpp 复制代码
//List.c文件

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

	free(del);
	del;
}

6、双向链表的查找

双向链表的查找就是把各个结点存储的数据打印出来看看。

代码实例:

cpp 复制代码
//List.h文件
//双向链表的打印
void LTPrint(LTNode* phead);
cpp 复制代码
//List.c文件
//双向链表的打印
void LTPrint(LTNode* phead)
{
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		printf("%d -> ", pcur->data);
		pcur = pcur->next;
	}
	printf("\n");
}

7、数据的查找

给一个数据在双向链表查找,有就返回存储数据的结点的地址,没有返回NULL。

代码实例:

cpp 复制代码
//List.h文件
//数据的查找
void LTFind(LTNode* phead, LTDataType x);
cpp 复制代码
//List.c文件
//数据的查找
void LTFind(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* pcur = phead->next;
	while (pcur != phead)//不打印head存储的数据
	{
		if (pcur->data == x)
		{
			return pcur;//找到了
		}
		pcur = pcur->next;//往下找
	}
	return NULL;//没有找到
}

8、在pos位置之后插入数据

如图六:

根据用户给出的地址在双向链表找到结点并且在结点后面插入一个结点。如图六,我们假设在node2的后面插入结点node4,那么受影响的有node4结点的前后指向、node3的前一个指向和node2的下一个指向。

代码实例:

cpp 复制代码
//List.h文件
//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x);
cpp 复制代码
//List.c文件
//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);
	LTNode* newnode = buyNode(x);
	newnode->prev = pos;
	newnode->next = pos->next;

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

9、删除pos位置的结点

如图七:

如图七我们假设删除node2结点,那么受影响的有node3的前一个结点和node1的下一个结点。

代码实例:

cpp 复制代码
//List.h文件
//删除pos位置的结点
void LTErase(LTNode* pos);
cpp 复制代码
//List.c文件
//删除pos位置的结点
void LTErase(LTNode* pos)
{
	assert(pos);
	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;

	free(pos);
	pos = NULL;
}

以上内容讲述结束,接下来是完整的代码:

cpp 复制代码
//List.h文件
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//定义双向链表的结构
typedef int LTDataType;
typedef struct ListNode
{
	LTDataType data;//存储的数据
	struct ListNode* next;//下一个结点
	struct ListNode* prev;//上一个结点
}LTNode;

//双向链表的初始化
LTNode* LTInit();
//尾插
void LTPushBack(LTNode* phead, LTDataType x);
//头插
void LTPushFront(LTNode* phead, LTDataType x);
//尾删
void LTPopBack(LTNode* phead);
//头删
void LTPopFront(LTNode* phead);
//双向链表的打印
void LTPrint(LTNode* phead);
//数据的查找
void LTFind(LTNode* phead, LTDataType x);
//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x);
//删除pos位置的结点
void LTErase(LTNode* pos);
cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#include"List.h"
//List.c文件
//申请结点
LTNode* buyNode(LTDataType x)
{
	LTNode* node = (LTNode*)malloc(sizeof(LTNode));
	if (node == NULL)
	{
		perror("malloc fail!");
	}
	node->data = x;
	node->next = node->prev = node;
	return node;
}

//初始化
LTNode* LTInit()
{
	LTNode* phead = buyNode(-1);
	return phead;
}
//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = buyNode(x);
	newnode->prev = phead->prev;
	newnode->next = phead;
	phead->prev->next = newnode;
	phead->prev = newnode;
}
//头插
void LTPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = buyNode(x);
	newnode->prev = phead;
	newnode->next = phead->next;

	phead->next->prev = newnode;
	phead->next = newnode;
}
bool LTEmpty(LTNode* phead)
{
	assert(phead);
	return phead->next == phead;
}
//尾删
void LTPopBack(LTNode* phead)
{
	assert(!LTEmpty(phead));
	LTNode* del = phead->prev;//先把head结点上一个结点的地址存储起来也就是微博的地址,以便后面释放
	del->prev->next = phead;//改变倒数第二个结点的下一个存储的地址
	phead->prev = del->prev;

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

	free(del);
	del;
}
//双向链表的打印
void LTPrint(LTNode* phead)
{
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		printf("%d -> ", pcur->data);
		pcur = pcur->next;
	}
	printf("\n");
}
//数据的查找
void LTFind(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* pcur = phead->next;
	while (pcur != phead)//不打印head存储的数据
	{
		if (pcur->data == x)
		{
			return pcur;//找到了
		}
		pcur = pcur->next;//往下找
	}
	return NULL;//没有找到
}
//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);
	LTNode* newnode = buyNode(x);
	newnode->prev = pos;
	newnode->next = pos->next;

	pos->next->prev = newnode;
	pos->next = newnode;
}
//删除pos位置的结点
void LTErase(LTNode* pos)
{
	assert(pos);
	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;

	free(pos);
	pos = NULL;
}

完!!

相关推荐
Chef_Chen1 分钟前
从0开始学习R语言--Day39--Spearman 秩相关
开发语言·学习·r语言
不学会Ⅳ9 分钟前
Mac M芯片搭建jdk源码环境(jdk24)
java·开发语言·macos
2401_8812444012 分钟前
牛客周赛99
c++
好开心啊没烦恼1 小时前
Python 数据分析:计算,分组统计1,df.groupby()。听故事学知识点怎么这么容易?
开发语言·python·数据挖掘·数据分析·pandas
lljss20202 小时前
Python11中创建虚拟环境、安装 TensorFlow
开发语言·python·tensorflow
山登绝顶我为峰 3(^v^)33 小时前
如何录制带备注的演示文稿(LaTex Beamer + Pympress)
c++·线性代数·算法·计算机·密码学·音视频·latex
Python×CATIA工业智造5 小时前
Frida RPC高级应用:动态模拟执行Android so文件实战指南
开发语言·python·pycharm
十五年专注C++开发6 小时前
CMake基础:条件判断详解
c++·跨平台·cmake·自动化编译
我叫小白菜6 小时前
【Java_EE】单例模式、阻塞队列、线程池、定时器
java·开发语言
狐凄6 小时前
Python实例题:基于 Python 的简单聊天机器人
开发语言·python