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

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

🐟🐟文章专栏:C++

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

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

你们的支持就是我创造的动力!!

今日思想:岁岁平,岁岁安,岁岁平安!

一、单链表的定义

单链表是线性表(具体说明:在附录有链接)的一种,逻辑结构:是线性的,物理结构:不一定是线性的。

二、单链表结构体的实现

我们先创建三个文件(test.c,SList.h,SList.c)其中test.c文件是对单链表的各种实现方法的测试,本文不详细测试。

我们在文件SList.h定义结构体的实现,重定义int类型是为了根据用户的数据类型来定义。

data是用户的数据,next指向下一个结构体的地址(地址不连续),我们可以通next找到下一个结点的内容。我们可以把单链表的形成是一辆火车如图(以下的所有方法来源这张图):

代码实例:

cpp 复制代码
//SList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

//定义链表的结构
//定义结点的结构

typedef int SLTDataType;
typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;//存储下一个结点(火车结点)的地址
}SLTNode;

三、单链表插入或者删除用户数据的方法实现

①打印链表的实现(用于观察代码的实现是否错误)

我们先手动构造一个链表当然我们要实现的单链表是不用一个个手动构造的,构造的目的:测试打印链表的代码实现是否错误。

手动构造链表的代码实现:

cpp 复制代码
//test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"
//手动构造一个链表
void test01()
{
	SLTNode* node1 = (SLTNode*)malloc(sizeof(SLTNode));
	SLTNode* node2 = (SLTNode*)malloc(sizeof(SLTNode));
	SLTNode* node3 = (SLTNode*)malloc(sizeof(SLTNode));
	SLTNode* node4 = (SLTNode*)malloc(sizeof(SLTNode));

	node1->data = 1;
	node2->data = 2;
	node3->data = 3;
	node4->data = 4;

	node1->next = node2;
	node2->next = node3;
	node3->next = node4;
	node4->next = NULL;

	SLTNode* plist = node1;
}

int main()
{
	test01();
	return 0;
}

代码解析:node1,node2等就是一节一节的车厢,如图:

打印链表的代码实现:

cpp 复制代码
//SList.h
//打印链表
void SLTPrint(SLTNode* phead);//phead头结点
cpp 复制代码
//SList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"
//打印链表
void SLTPrint(SLTNode* phead)
{
	SLTNode* pcur = phead;
	while (pcur != NULL)
	{
		printf("%d -> ", pcur->data);
		pcur = pcur->next;
	}
	printf("NULL\n");
}

我们在test.c文件调用上面这个函数,代码如下:

cpp 复制代码
//test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"
//手动构造一个链表
void test01()
{
	SLTNode* node1 = (SLTNode*)malloc(sizeof(SLTNode));
	SLTNode* node2 = (SLTNode*)malloc(sizeof(SLTNode));
	SLTNode* node3 = (SLTNode*)malloc(sizeof(SLTNode));
	SLTNode* node4 = (SLTNode*)malloc(sizeof(SLTNode));

	node1->data = 1;
	node2->data = 2;
	node3->data = 3;
	node4->data = 4;

	node1->next = node2;
	node2->next = node3;
	node3->next = node4;
	node4->next = NULL;

	SLTNode* plist = node1;
	SLTPrint(plist);//node是一个地址,只要通过next就能找到下一个结点。
}

链表的打印如图:

②尾插(在最后一个结点前插入一个结点)

从这里开始我们就不用手动构造链表了,那么我们怎么创建结点呢??在后面的各种方法我们只要插入数据就会自动申请一个结点,即调用一下SLTNode* SLTBuyNode(SLTDataType x)函数,不用手动构造结点。

创建结点的代码实现(因为申请结点在SList.c中申请):

cpp 复制代码
//SList.c
//向操作系统申请一个新结点
SLTNode* SLTBuyNode(SLTDataType x)//x;用户的data(数据)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	//判断是否创建结点成功
	if (newnode == NULL)
	{
		//创建失败
		perror("malloc fail!");
		exit(1);
	}
	//成功
	newnode->data = x;//存储用户数据
	newnode->next = NULL;

	return newnode;
}

在尾插数据之前我们要先申请一个结点

尾插代码实例:

cpp 复制代码
//SList.h
//尾插
void SLTPushBack(SLTNode* pphead,SLTDataType x);
cpp 复制代码
//SList.c
//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)//在test.c文件调用这个函数时我们传址调用,原因我们要改变整个链表的构造,不是改值,所以这里用二级指针来接收
{
	assert(pphead);
	SLTNode* newnode = SLTBuyNode(x);
	//链表为空,phead直接指向newnode结点
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//链表不为空,找尾结点,将尾结点和新结点连接起来
		SLTNode* ptail = *pphead;
		while (ptail->next)//等价于ptail->next!=NULL;
		{
			ptail = ptail->next;
		}
		ptail->next = newnode;
	}
}

③头插(在第一个结点前插入用户的数据)

代码实例:

cpp 复制代码
//SList.h
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x);

只要关于插入数据必须申请结点

cpp 复制代码
//SList.c
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);//判断是否为空链表
	SLTNode* newnode = SLTBuyNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

④尾删(删除最后一个结点)

代码实例:

cpp 复制代码
//SList.h
//尾删
void SLTPopBack(SLTNode** pphead);
cpp 复制代码
//SList.c
//尾删
void SLTPopBack(SLTNode** pphead)
{
	assert(pphead && *pphead);
	//特殊情况:只有一个节点的时候
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else//多个结点
	{
		SLTNode* prev = NULL;
		SLTNode* ptail = *pphead;//不改变*pphead的值的情况下,找到最后一个链表
		while (ptail->next)//找最后一个结点
		{
			prev = ptail;
			ptail = ptail->next;
		}
		prev->next = NULL;
		free(ptail);//释放最后一个链表的空间
		ptail = NULL;
	}
}

⑤头删(删除第一个结点)

代码实例:

cpp 复制代码
//SList.h
//头删
void SLTPopFront(SLTNode** pphead);
cpp 复制代码
//SList.c
//头删
void SLTPopFront(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* next = (*pphead)->next;//->的优先级高于*
	free(*pphead);//释放第一个结点
	*pphead = next;//此时第二个结点就是第一个结点
}

⑥查找用户是否存在x的值

代码实例:

cpp 复制代码
//SList.h
//查找是否存在用户x的值
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
cpp 复制代码
//SList.c
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)//传值调用,不改变链表
{
	SLTNode* pcur = phead;
	while (pcur)
	{
		if (pcur->data == x)
		{
			return pcur;//如果有用户x的值返回存储x的结点的地址
		}
		pcur = pcur->next;
	}
	return NULL;//没有返回空指针
}

⑦在指定位置之前插入数据

代码实例:

cpp 复制代码
//SList.h
//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
cpp 复制代码
//SList,c
//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)//pos是个地址,在这个地址前插入数据
{
	SLTNode* newnode = SLTBuyNode(x);//申请新结点
	assert(pphead && pos);
	if (pos == *pphead)//判断pos是否是第一个地址,如果是那就是头插
	{
		//头插
		SLTPushFront(pphead, x);
	}
	else
	{
		SLTNode* prev = *pphead;//防止*pphead不指向首地址
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		newnode->next = pos;
		prev->next = newnode;
	}
}

⑧在指定位置之后插入数据

代码实例:

cpp 复制代码
//SList.h
//在指定位置之后插入数据
void SLTInsertAfert(SLTNode* pos, SLTDataType x);
cpp 复制代码
//SList.c
//在指定位置之后插入数据
void SLTInsertAfert(SLTNode* pos, SLTDataType x)
{
	assert(pos);
	SLTNode* newnode = SLTBuyNode(x);
	newnode->next = pos->next;//pos->next存储的下一个结点的地址,把新创建结点的next指向pos->next就行
	pos->next = newnode;//把新创建的结点的地址给pos->next
}

⑨删除pos结点

代码实例:

cpp 复制代码
//SList.h
//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos);
cpp 复制代码
//SList.c
//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos)//pos是个地址,删除pos这个地址的结点
{
	assert(pphead && pos);
	if (pos == *pphead)//判断pos是否是头结点的地址,如果是就是头删
	{
		SLTPopFront(pphead);//头删的函数调用(上面有)
	}
	else
	{
		SLTNode* prev = *pphead;//防止改变头结点的地址
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

⑩删除pos之后的结点

代码实例:

cpp 复制代码
//SList.h
//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos);
cpp 复制代码
//SList.c
//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos)//pos是个地址,删除pos这个地址的结点后面的结点
{
	assert(pos && pos->next);
	SLTNode* del = pos->next;//del是pos后面的结点的地址
	pos->next = del->next;//del->next相当于pos->next->next,先把del的next的地址给pos->next,再释放del
	free(del);//释放pos后面结点的空间,就是del
	del = NULL;
}

11.销毁链表

代码实例:

cpp 复制代码
//SList.h
//销毁链表
void SListDestroy(SLTNode** pphead);
cpp 复制代码
//SList.c
//销毁链表(把链表的各个结点的空间释放)
void SListDestroy(SLTNode** pphead)
{
	assert(pphead);
	SLTNode* pcur = *pphead;
	while (pcur)//分别把各个结点的空间释放
	{
		SLTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

完!!

附录

【C++】数据结构 顺序表的实现(详解)_数据结构c++实现-CSDN博客

相关推荐
泯泷几秒前
JS代码混淆器:JavaScript obfuscator 让你的代码看起来让人痛苦
开发语言·javascript·ecmascript
轩源源4 分钟前
C++草原三剑客之一:继承
开发语言·数据结构·c++·算法·青少年编程·继承·组合
未知陨落9 分钟前
leetcode题目(1)
c++·leetcode
每天写点bug2 小时前
【go每日一题】 责任链模式的实现
开发语言·golang·责任链模式
半盏茶香2 小时前
C语言勘破之路-最终篇 —— 预处理(下)
c语言·开发语言·c++·算法
时雨h2 小时前
30天面试打卡计划 2024-12-25 26 27 面试题
java·开发语言·数据库
别致的影分身3 小时前
Linux 线程池
java·开发语言·jvm
山山而川粤5 小时前
母婴用品系统|Java|SSM|JSP|
java·开发语言·后端·学习·mysql
_君莫笑6 小时前
【视频】将yuv420p的一帧数据写入文件
c++·音视频·yuv420p
迷失蒲公英6 小时前
XML与Go结构互转实现(序列化及反序列化)
xml·开发语言·golang