数据结构-->线性表-->单链表

 

链表的定义

链表:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

与顺序表不同的是,链表里的每节都是独立申请下来的空间,我们称之为“节点、结点”。

节点的组成主要由两个部分:当前节点要保存的数据和保存下一个节点的地址(指针变量)。

链表的节点(结点)

链表中的每个节点都是独立申请的(需要插入数据时才去申请一块节点的空间),我们需要通过指针变量来保存下一个节点位置才能从当前节点找到下一个节点。

给出每个节点对应的结构体代码:以保存的节点是整形为例:

struct SListNode
{int data; //节点数据struct SListNode* next; //指针变量⽤保存下⼀个节点的地址
};

当我们想要保存一个整型数据时,实际是向操作系统申请了一块内存,这个内存不仅要保存整型数据,也需要保存下一个节点的地址(当下一个节点为空时保存的地址为空)。

链式结构在逻辑上是连续的,在物理结构上不一定连续

节点一般是从堆上申请的

从堆上申请来的空间,是按照一定策略分配出来的,每次申请的空间可能连续,可能不连续。

链表的分类 

对于链表的分类来说:一共有8种

最常用的两种链表结构 

虽然链表结构很多,但最常用的只有两种结构
单链表:也叫无头单向非循环链表,结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构。
带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。这个链表虽然结构复杂,但是使用代码实现以后会发现法结构会带来更多优势。

 

首先我们给出我们要实现的单链表的头文件:

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLDataType;
typedef struct SList
{SLDataType data;struct SList* next;
}SL;
//打印
void SLPrint(SL* phead);
//销毁
void SLDestroy(SL** pphead);
//开辟新链表节点
SL* Buynode(SLDataType x);
//链表头插
void SLPushFront(SL** pphead,SLDataType x);
//链表尾插
void SLPushBack(SL** pphead,SLDataType x);
//链表头删
void SLPopFront(SL** pphead); 
//链表尾删
void SLPopBack(SL** pphead);
//链表对节点的查找
SL* SLFind(SL** pphead,SLDataType x);
//对指定位置之前插入数据
void SLInsert(SL** pphead,SL* pos,SLDataType x);
//在指定位置之后插入数据
void SLInsertafter(SL* pos, SLDataType x);
//删除pos节点
void SLErase(SL** pphead,SL* pos);
//删除pos之后的节点
void SLEraseafter(SL* pos);

对单链表打印


这里直接进行遍历就ok

//打印
void SLPrint(SL* phead)
{SL* pcur = phead;while (pcur){printf("%d->", pcur->data);pcur = pcur->next;}printf("NULL");
}

销毁单链表 

逐个销毁,销毁某一个节点时,保存他的下一个节点的地址。

//销毁
void SLDestroy(SL** pphead)
{assert(pphead);assert(*pphead);SL* pcur = *pphead;while (pcur){SL* next = (*pphead)->next;free(pcur);pcur = next;}*pphead = NULL;
}

 开辟节点空间

为新的数据开辟空间

//开辟节点空间
SL* Buynode(SLDataType x)
{SL* newnode = (SL*)malloc(sizeof(SL));if (newnode == NULL){perror("malloc fail\n");return;}newnode->data = x;newnode->next = NULL;return newnode;
}

 单链表头插

记得先后顺序,个人感觉容易犯错

//链表头插
void SLPushFront(SL** pphead, SLDataType x)
{assert(pphead);SL* new = Buynode(x);//个人觉得易错new->next = *pphead;*pphead = new;
}

单链表尾插

如果头结点为空,则相当于头插

如果头结点不为空,就正常找尾节点然后插入。

//链表尾插
void SLPushBack(SL** pphead, SLDataType x)
{assert(pphead);SL* new = Buynode(x);if (*pphead == NULL){*pphead = new;return;}SL* pcur = *pphead;while (pcur->next){pcur = pcur->next;}pcur->next = new;
}

 单链表头删

 对于删除来说,需要断言pphead和*pphead,pphead是存放*pphead的地址不能为NULL,

*pphead是为了判断单链表不能为NULL

//链表头删
void SLPopFront(SL** pphead)
{assert(pphead);assert(*pphead);SL* next = (*pphead)->next;free(*pphead);*pphead = next;
}

 单链表尾删

如果只有一个元素,就相当于头删,否则就相当于尾删

//链表尾删
void SLPopBack(SL** pphead)
{assert(pphead);assert(*pphead);if (((*pphead)->next) == NULL){free(*pphead);*pphead = NULL;}SL* prev = NULL;SL* pcur = *pphead;while (pcur->next){prev = pcur;pcur = pcur->next;}free(pcur);pcur = NULL;prev->next = NULL;
}

单链表对节点的查找

遍历查找与目标值相同的,然后返回存储该值的地址

//链表对节点的查找
SL* SLFind(SL** pphead, SLDataType x)
{assert(pphead);SL* pcur = *pphead;while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}

对指定位置插入数据

1.pos在*pphead,相当于头插

2.pos不在*pphead,就正常插入

//对指定位置之前插入数据
void SLInsert(SL** pphead, SL* pos, SLDataType x)
{assert(pphead);assert(pos);assert(*pphead);SL* new = Buynode(x);if (pos == *pphead){SLPushFront(pphead, x);return;}SL* pcur = *pphead;while (pcur->next!=pos){pcur = pcur->next;}pcur->next = new;new->next = pos;
}

 在指定位置后插入

没什么好说的,直接找到指定位置,然后插入即可

//在指定位置之后插入数据
void SLInsertafter(SL* pos, SLDataType x)
{assert(pos);SL* new = Buynode(x);new->next = pos->next;pos->next = new;
}

 删除pos节点的值

如果pos为*pphead就头删,否则就正常删除pos节点的值

//删除pos节点
void SLErase(SL** pphead, SL* pos)
{assert(pphead);assert(*pphead);assert(pos);if (*pphead == pos){SLPopFront(pphead);return;}SL* pcur = *pphead;while (pcur->next != pos){pcur = pcur->next;}SL* next = pos->next;pcur->next = next;free(pos);pos=NULL;
}

 删除pos节点之后的值

找到pos节点,当然联系pos和pos->next->next的值

//删除pos之后的节点
void SLEraseafter(SL* pos)
{assert(pos);assert(pos->next);SL* pcur = pos->next;pos->next = pos->next->next;free(pcur);pcur = NULL;
}

 最终代码

SList.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLDataType;
typedef struct SList
{SLDataType data;struct SList* next;
}SL;
//打印
void SLPrint(SL* phead);
//销毁
void SLDestroy(SL** pphead);
//开辟新链表节点
SL* Buynode(SLDataType x);
//链表头插
void SLPushFront(SL** pphead,SLDataType x);
//链表尾插
void SLPushBack(SL** pphead,SLDataType x);
//链表头删
void SLPopFront(SL** pphead); 
//链表尾删
void SLPopBack(SL** pphead);
//链表对节点的查找
SL* SLFind(SL** pphead,SLDataType x);
//对指定位置之前插入数据
void SLInsert(SL** pphead,SL* pos,SLDataType x);
//在指定位置之后插入数据
void SLInsertafter(SL* pos, SLDataType x);
//删除pos节点
void SLErase(SL** pphead,SL* pos);
//删除pos之后的节点
void SLEraseafter(SL* pos);

SList.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "SList.h"
//打印
void SLPrint(SL* phead)
{SL* pcur = phead;while (pcur){printf("%d->", pcur->data);pcur = pcur->next;}printf("NULL");
}
//销毁
void SLDestroy(SL** pphead)
{assert(pphead);assert(*pphead);SL* pcur = *pphead;while (pcur){SL* next = (*pphead)->next;free(pcur);pcur = next;}*pphead = NULL;
}
//开辟节点空间
SL* Buynode(SLDataType x)
{SL* newnode = (SL*)malloc(sizeof(SL));if (newnode == NULL){perror("malloc fail\n");return;}newnode->data = x;newnode->next = NULL;return newnode;
}
//链表头插
void SLPushFront(SL** pphead, SLDataType x)
{assert(pphead);SL* new = Buynode(x);//个人觉得易错new->next = *pphead;*pphead = new;
}
//链表尾插
void SLPushBack(SL** pphead, SLDataType x)
{assert(pphead);SL* new = Buynode(x);if (*pphead == NULL){*pphead = new;return;}SL* pcur = *pphead;while (pcur->next){pcur = pcur->next;}pcur->next = new;
}
//链表头删
void SLPopFront(SL** pphead)
{assert(pphead);assert(*pphead);SL* next = (*pphead)->next;free(*pphead);*pphead = next;
}
//链表尾删
void SLPopBack(SL** pphead)
{assert(pphead);assert(*pphead);if (((*pphead)->next) == NULL){free(*pphead);*pphead = NULL;}SL* prev = NULL;SL* pcur = *pphead;while (pcur->next){prev = pcur;pcur = pcur->next;}free(pcur);pcur = NULL;prev->next = NULL;
}
//链表对节点的查找
SL* SLFind(SL** pphead, SLDataType x)
{assert(pphead);SL* pcur = *pphead;while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}
//对指定位置之前插入数据
void SLInsert(SL** pphead, SL* pos, SLDataType x)
{assert(pphead);assert(pos);assert(*pphead);SL* new = Buynode(x);if (pos == *pphead){SLPushFront(pphead, x);return;}SL* pcur = *pphead;while (pcur->next!=pos){pcur = pcur->next;}pcur->next = new;new->next = pos;
}
//在指定位置之后插入数据
void SLInsertafter(SL* pos, SLDataType x)
{assert(pos);SL* new = Buynode(x);new->next = pos->next;pos->next = new;
}
//删除pos节点
void SLErase(SL** pphead, SL* pos)
{assert(pphead);assert(*pphead);assert(pos);if (*pphead == pos){SLPopFront(pphead);return;}SL* pcur = *pphead;while (pcur->next != pos){pcur = pcur->next;}SL* next = pos->next;pcur->next = next;free(pos);pos=NULL;
}
//删除pos之后的节点
void SLEraseafter(SL* pos)
{assert(pos);assert(pos->next);SL* pcur = pos->next;pos->next = pos->next->next;free(pcur);pcur = NULL;
}

Test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "SList.h"
int main()
{SL* plist = NULL;SLPushBack(&plist, 1);SLPushBack(&plist, 2);//1->2//SLPrint(plist);SLPushFront(&plist, 3);//3->1->2//SLPrint(plist);SLPushFront(&plist, 4);//SLPrint(plist);//4->3->1->2SLPopBack(&plist);//4->3->1//SLPrint(plist);SLPopFront(&plist);//3->1//SLPrint(plist);SL* new=SLFind(&plist, 3);SLInsert(&plist,new,4);//4->3->1//SLPrint(plist);SLInsertafter(new, 5);//4->3->5->1//SLPrint(plist);SLErase(&plist, new);//4->5->1SLPrint(plist);SLDestroy(&plist);return 0;
}

运行test函数:

以及测试过了,每个接口函数都没啥问题

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/676564.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【深度学习】:滴滴出行-交通场景目标检测

清华大学驭风计划课程链接 学堂在线 - 精品在线课程学习平台 (xuetangx.com) 代码和报告均为本人自己实现&#xff08;实验满分&#xff09;&#xff0c;只展示主要任务实验结果&#xff0c;如果需要详细的实验报告或者代码可以私聊博主&#xff0c;接实验技术指导1对1 有任…

C++学习Day04之单例模式

目录 一、程序及输出1.1 饿汉式实例1.2 饿汉式单例1.3 懒汉式单例1.4 线程安全的懒汉式单例 二、分析与总结 一、程序及输出 1.1 饿汉式实例 #include<iostream> using namespace std; #include <string> class Printer { public:static Printer * getInstance()…

零基础学编程怎么入手,中文编程工具构件箱之渐变背景构件用法教程,系统化的编程视频教程上线

零基础学编程怎么入手&#xff0c;中文编程工具构件箱之渐变背景构件用法教程&#xff0c;系统化的编程视频教程上线 一、前言 今天给大家分享的中文编程开发语言工具资料如下&#xff1a; 编程入门视频教程链接 https://edu.csdn.net/course/detail/39036 编程工具及实例…

Python速成篇(基础语法)下(新年快乐♥)

引言 一天不学编程手就痒&#xff0c;今天是除夕&#xff0c;学C艹vector的话就没时间出去玩了&#xff0c;所以就写写博客。今天要讲的内容是关于&#xff0c;list&#xff08;列表&#xff09;&#xff0c;tuple&#xff08;元组&#xff09;&#xff0c;字典&#xff08;di…

005集——shp格式数据转换乱码问题——arcgis

shp数据格式与其他数据格式转换过程中会遇到乱码等问题&#xff0c;原因如下&#xff1a; 在Shapefile头文件&#xff08;dBase Header&#xff09;中&#xff0c;一般会包含字符编码信息&#xff0c;这个信息称为 LDID &#xff08; Language Driver ID&#xff09;。在使用ar…

python3 获取某个文件夹所有的pdf文件表格提取表格并一起合并到excel文件

下面是一个完整的示例&#xff0c;其中包括了merge_tables_to_excel函数的定义&#xff0c;并且假设该函数的功能是从每个PDF文件中提取第一个表格并将其合并到一个Excel文件中&#xff1a; import os from pathlib import Path import pandas as pd import pdfplumber …

leetcode 3027. 人员站位的方案数 II【离散化前缀和+枚举】

原题链接&#xff1a;3027. 人员站位的方案数 II 题目描述&#xff1a; 给你一个 n x 2 的二维数组 points &#xff0c;它表示二维平面上的一些点坐标&#xff0c;其中 points[i] [xi, yi] 。 我们定义 x 轴的正方向为 右 &#xff08;x 轴递增的方向&#xff09;&#x…

vue-内置组件-Suspense

Suspense (实验性功能) <Suspense> 是一项实验性功能。它不一定会最终成为稳定功能&#xff0c;并且在稳定之前相关 API 也可能会发生变化。 <Suspense> 是一个内置组件&#xff0c;用来在组件树中协调对异步依赖的处理。它让我们可以在组件树上层等待下层的多个嵌…

[word] word2019段落中创建纵横混排的方法图解教程 #知识分享#其他#职场发展

word2019段落中创建纵横混排的方法图解教程 有时候在word文档中需要让文字纵横混排&#xff0c;word2019正好为我们带来了纵横混排的功能了&#xff0c;今天我们就来给大家介绍一下word2019段落中创建纵横混排的方法。 步骤1&#xff1a;打开Word文档&#xff0c;选中需要纵向…

ARM:AI 的翅膀,还能飞多久?

ARM&#xff08;ARM.O&#xff09;于北京时间 2024 年 2 月 8 日上午的美股盘后发布了 2024 年第三财年报告&#xff08;截止 2023 年 12 月&#xff09;&#xff0c;要点如下&#xff1a; 1、整体业绩&#xff1a;收入再创新高。ARM 在 2024 财年第三季度&#xff08;即 23Q4…

python-pandas查漏补缺

1. create labels for Series 2. 3. 4. 用平均数等去填empty的格子 5. 6. 7.

旅游|基于Springboot的旅游管理系统设计与实现(源码+数据库+文档)

旅游管理系统目录 目录 基于Springboot的旅游管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、用户管理 2、景点分类管理 3、景点信息管理 4、酒店信息管理 5、景点信息 6、游记分享管理 四、数据库设计 1、实体ER图 2、具体的表设计如下所示&#xf…

037 稀疏数组

代码示例 /*** 生成稀疏数组* param arr 原数组* param defaultValue 数组默认值* return*/ static int[][] extractArray(int[][] arr, int defaultValue) {// 统计有多少个非默认值int count 0;for (int i 0; i < arr.length; i) {for (int j 0; j < arr[i].lengt…

谷歌发布AI新品Gemini及收费模式;宜家推出基于GPT的AI家装助手

&#x1f989; AI新闻 &#x1f680; 谷歌发布AI新品Gemini及收费模式 摘要&#xff1a;谷歌宣布将原有的AI产品Bard更名为Gemini&#xff0c;开启了谷歌的AI新篇章。同时推出了强化版的聊天机器人Gemini Advanced&#xff0c;支持更复杂的任务处理&#xff0c;提供了两个月的…

Mysql Day03

多表设计 一对多 在多的一方添加外键约束&#xff0c;关联另外一方主键 一对一 任意一方添加外键约束&#xff0c;关联另外一方主键 多对多 建立第三张中间表&#xff0c;中间表至少包含两个外键&#xff0c;分别关联两方主键 idstu_idcourse_id 1 11 2 12313421524 案…

融资项目——获取树形结构的数据

如下图所示&#xff0c;下列数据是一个树形结构数据&#xff0c;行业中包含若干子节点。表的设计如下图&#xff0c;设置了一个id为1的虚拟根节点。&#xff08;本树形结构带虚拟根节点共三层&#xff09; 实现逻辑&#xff1a; 延时展示方法&#xff0c;先展现第二层的信息&a…

机器学习8-决策树

决策树&#xff08;Decision Tree&#xff09;是一种强大且灵活的机器学习算法&#xff0c;可用于分类和回归问题。它通过从数据中学习一系列规则来建立模型&#xff0c;这些规则对输入数据进行递归的分割&#xff0c;直到达到某个终止条件。 决策树的构建过程&#xff1a; 1.…

2024年生成式AI芯片市场规模将达500亿美元

1月24日&#xff0c;德勤发布《2024科技、传媒和电信行业预测》中文版报告&#xff0c;2024年是科技、传媒和电信行业关键的一年&#xff0c;不少科技公司正利用生成式AI升级软件和服务&#xff0c;预计今年全球生成式人工智能芯片销售额可能达到500亿美元以上。 2024年将有许…

【开源】基于JAVA+Vue+SpringBoot的智慧社区业务综合平台

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 业务类型模块2.2 基础业务模块2.3 预约业务模块2.4 反馈管理模块2.5 社区新闻模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 业务类型表3.2.2 基础业务表3.2.3 预约业务表3.2.4 反馈表3.2.5 社区新闻表 四、系统展…

Mysql一行记录存储过程

Mysql一行记录存储过程 Mysql的文件架构 行&#xff08;row&#xff09; 数据库表中的记录都是行存放的&#xff0c;每行继续根据不同的行格式都有不同的存储结构。 页&#xff08;page&#xff09; 记录是按照行来存储的&#xff0c;但是数据库的读取是以页为单位的&…