本篇博客给大家带来的是用C++语言来实现数据结构的双向链表(企业存储用户数据的实现)
🐟🐟文章专栏:C++
🚀🚀若有问题评论区下讨论,我会及时回答
❤❤欢迎大家点赞、收藏、分享
先给大家说一声抱歉,之前被学校安排去上海做服务员了,没有更新,请大家多多包涵!
今日思想:曙光之前尽是黑暗!
一、双向链表的性质
双向链表是线性的,逻辑结构:是线性的,物理结构:不一定线性。
二、双向链表的定义
图一:
注意:这里的head结点是不存储数据的,由上图所示我们可知双向链表通过存储前后结点的地址来实现循环,可见双向链表是可以往前或者往后遍历的。
三、双向链表的实现
实现方法:双向链表的初始化、尾插、头插、尾删、头删、在pos(指定位置)之后插入数据、在pos位置、删除pos位置的结点、双向链表的打印。
1、双向链表的初始化
如图一所示我们我们先创造一个结构体,这个结构体包含前一个结点的地址、存储的数据、下一个结点的地址。
代码实例:
//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;
我们要进行初始化,头结点是不存储任何数据的,那么接下来的头插和尾插等要申请结点来存储数据,所以可以利用这一点我们对头结点进行初始化。
代码实例:
//List.h文件
//双向链表的初始化
LTNode* LTInit();
#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的前地址的指向。
代码实例:
//List.h文件
//尾插
void LTPushBack(LTNode* phead, LTDataType x);
//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的后面插入,那么受影响只有新结点,改变前后地址的指向就行。
代码实例:
//List.h文件
//头插
void LTPushFront(LTNode* phead, LTDataType x);
//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结点的下一个指向。
代码实例:
//List.h文件
//尾删
void LTPopBack(LTNode* phead);
//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的上一个指向。
代码实例:
//List.h文件
//头删
void LTPopFront(LTNode* phead);
//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、双向链表的查找
双向链表的查找就是把各个结点存储的数据打印出来看看。
代码实例:
//List.h文件
//双向链表的打印
void LTPrint(LTNode* phead);
//List.c文件
//双向链表的打印
void LTPrint(LTNode* phead)
{
LTNode* pcur = phead->next;
while (pcur != phead)
{
printf("%d -> ", pcur->data);
pcur = pcur->next;
}
printf("\n");
}
7、数据的查找
给一个数据在双向链表查找,有就返回存储数据的结点的地址,没有返回NULL。
代码实例:
//List.h文件
//数据的查找
void LTFind(LTNode* phead, LTDataType x);
//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的下一个指向。
代码实例:
//List.h文件
//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x);
//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的下一个结点。
代码实例:
//List.h文件
//删除pos位置的结点
void LTErase(LTNode* pos);
//List.c文件
//删除pos位置的结点
void LTErase(LTNode* pos)
{
assert(pos);
pos->next->prev = pos->prev;
pos->prev->next = pos->next;
free(pos);
pos = NULL;
}
以上内容讲述结束,接下来是完整的代码:
//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);
#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;
}
完!!