链表的实现(文末附完整代码)

链表的概念及结构

链表是一种物理存储结构上非连续、非顺序的存储结构数据元素的逻辑顺序是通过链表中的指针链接次序实现的
我们在上一篇文章所学习的顺序表是连续存储的
例如:
顺序表就好比火车上的一排座位,是连续的
而链表就好比是火车的各节车厢,中间有东西将其互相连接的
在这里插入图片描述
链表的基本结构图如下:
有一个指针指向下一个节点
在这里插入图片描述

链表的概念及结构

实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:
链表可以是单向和双向,循环和不循环,带头和不带头,这样一组合,就会出现八种类型的列表
单向的列表如下:
在这里插入图片描述
双向列表:
相比较单向,双向的增删查改较为容易,他会自带一个prev的节点,能顾标记当前节点的前一个节点
在这里插入图片描述
循环列表:
其实循环列表就是最后一个节点指向了开头节点
在这里插入图片描述
带头和不带头:
头节点我们可以称其为哨兵位,它是不会在链表中存储有效的数据的
在这里插入图片描述
其实八种链表我们最常用的只有两种:
无头单向非循环链表:
在这里插入图片描述
带头双向循环列表:
在这里插入图片描述

  1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构
  2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是后面的学习中你会发现其实他是比较简单的

链表的实现

首先我们要了解的就是单链表的实现:
头文件如下:

#include<stdio.h>
#include<assert.h>
#include<ctype.h>
#include<string.h>
#include<stdlib.h>
typedef int SLTDateType;
typedef struct SListNode
{SLTDateType data;struct SListNode* next;
}SListNode;// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos);
// 单链表的销毁
void SListDestroy(SListNode** plist);

新节点的创建:
我们要创建一个节点,首先就是要为他动态开辟一个空间
然后再将这个新节点newnode的值赋予x,并且将他的next置为空,然后函数返回这个节点

SListNode* BuySListNode(SLTDateType x)
{SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));if (newnode == NULL){perror("malloc");return NULL;}newnode->data = x;newnode->next = NULL;return newnode;
}

链表的打印:
打印链表是我们可以用一个符号->来代替空格,但是链表实际上是没有这个符号的
我们可以首先定义cur,从链表的第一个节点开始遍历,知道cur为空时,就不会打印了,并且打印一次cur的data,cur要等于cue的next

void SListPrint(SListNode* plist)
{SListNode* cur = plist;while (cur != NULL){printf("%d->", cur->data);cur = cur->next;}printf("NULL\n");
}

单链表的尾插:
这里我们要注意的是要记得用二级指针,因为当链表为空时,我们要改变的是节点的地址,而我们要改变地址,就要用地址的地址,也就是二级指针
首先,需要插入一个节点我们要做的就是创建一个新节点,我们之前定义了的一个函数直接使用
然后我们创建一个tail指针,让他从链表的第一个位置开始遍历,一直遍历到最后一个节点,此时令tail的next等于newnode即可

void SListPushBack(SListNode** pplist, SLTDateType x)
{assert(pplist);/*assert(*pplist);*///链表为空时可以尾插SListNode* newnode = BuySListNode(x);if (*pplist == NULL){*pplist = newnode;}else{SListNode* tail = *pplist;while (tail->next){tail = tail->next;}tail->next = newnode;}
}

单链表的头插:
头插比较简单,我们直接将新节点的next等于链表的第一个节点即可,也就是*pplist,我们传进来的是**pplist,是节点地址的地址,所以我们需要解引用一次才能将地址给newnode的next

void SListPushFront(SListNode** pplist, SLTDateType x)
{SListNode* newnode = BuySListNode(x);newnode->next = *pplist;*pplist = newnode;
}

单链表的尾删:
尾删的情况我们要分为两种:
1.只有一个节点时:
只有一个节点时我们直接free掉这个节点,其次为了防止野指针,我们要将其置空
2.当有多个节点时:
我们创建一个tail和prev,然后用循环将tail遍历到最后一个节点,循环的终止条件时tail->next为空,条件满足时就将tail赋予prev,当跳出循环时,prev就是尾节点的前一个节点,我们直接将tail给free掉,将其置空,这样尾节点就被删除了
在这里插入图片描述

void SListPopBack(SListNode** pplist)
{assert(*pplist);//链表为空时不能再删,暴力检查 if ((*pplist)->next == NULL){free(*pplist);*pplist = NULL;}else{SListNode* prev = NULL;SListNode* tail = *pplist;while (tail->next){prev = tail;tail = tail->next;}free(tail);prev->next = NULL;}
}

单链表的头删:
头删就很简单了,首先定义一个tail存入第一个节点的位置,然后将第一个节点的位置移动到他的next,注意记得将临时存储第一个节点位置的指针给free掉,避免出现野指针的问题

void SListPopFront(SListNode** pplist)
{assert(*pplist);//链表为空时不能再删,暴力检查SListNode* tail = *pplist;*pplist = (tail)->next;free(tail);
}

节点的查找:
节点的查找也比较容易,直接用while循环遍历,如果cur的data和x相等,就返回cur,否则就是没有这个节点,返回null

SListNode* SListFind(SListNode* plist, SLTDateType x)
{SListNode* cur = plist;while (cur->next){if (cur->data == x){return cur;//break;}cur = cur->next;}return NULL;
}

在一个节点之后插入节点:
需要插入节点首先要做的就是创建一个新节点
然后首先要做的是将newnode的next连接到要插入位置pos的next
再将pos的next置为newnode
这里注意,位置不能颠倒,不然newnode的next就找不到了
在这里插入图片描述

void SListInsertAfter(SListNode* pos, SLTDateType x)
{assert(pos);SListNode* newnode = BuySListNode(x);newnode->next = pos->next;pos->next = newnode;
}

删除一个节点之后的节点:
要删除节点之后的一个节点,首先这个节点之后一定要有一个节点,所以要对pos->next进行断言
我们将pos->next存储到next中,然后将pos->next直接指向next->next即可,也就是把pos->next=pos->next->next
在这里插入图片描述

void SListEraseAfter(SListNode* pos)
{assert(pos);assert(pos->next);//pos->next = pos->next->next;SListNode* next = pos->next;pos->next = next->next;free(next);
}

单链表的销毁:
直接将pplist置空即可

void SListDestroy(SListNode** pplist)
{SListNode* node = *pplist;node = NULL;free(node);
}

好了,今天的分享到这里就结束了,感谢大家的支持!
完整代码如下:

SListNode* BuySListNode(SLTDateType x)
{SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));if (newnode == NULL){perror("malloc");return NULL;}newnode->data = x;newnode->next = NULL;return newnode;
}void SListPrint(SListNode* plist)
{SListNode* cur = plist;while (cur != NULL){printf("%d->", cur->data);cur = cur->next;}printf("NULL\n");
}void SListPushBack(SListNode** pplist, SLTDateType x)
{assert(pplist);/*assert(*pplist);*///链表为空时可以尾插SListNode* newnode = BuySListNode(x);if (*pplist == NULL){*pplist = newnode;}else{SListNode* tail = *pplist;while (tail->next){tail = tail->next;}tail->next = newnode;}
}void SListPushFront(SListNode** pplist, SLTDateType x)
{SListNode* newnode = BuySListNode(x);newnode->next = *pplist;*pplist = newnode;
}void SListPopBack(SListNode** pplist)
{assert(*pplist);//链表为空时不能再删,暴力检查 //SListNode* tail = pplist;//while (tail->next->next)//{//	tail = tail->next;//}//free(tail->next);//tail->next = NULL;if ((*pplist)->next == NULL){free(*pplist);*pplist = NULL;}else{SListNode* prev = NULL;SListNode* tail = *pplist;while (tail->next){prev = tail;tail = tail->next;}free(tail);prev->next = NULL;}
}void SListPopFront(SListNode** pplist)
{assert(*pplist);//链表为空时不能再删,暴力检查SListNode* tail = *pplist;*pplist = (tail)->next;free(tail);
}SListNode* SListFind(SListNode* plist, SLTDateType x)
{SListNode* cur = plist;while (cur->next){if (cur->data == x){return cur;//break;}cur = cur->next;}return NULL;
}void SListInsertAfter(SListNode* pos, SLTDateType x)
{assert(pos);SListNode* newnode = BuySListNode(x);newnode->next = pos->next;pos->next = newnode;
}void SListEraseAfter(SListNode* pos)
{assert(pos);assert(pos->next);//pos->next = pos->next->next;SListNode* next = pos->next;pos->next = next->next;free(next);
}void SListDestroy(SListNode** pplist)
{SListNode* node = *pplist;node = NULL;free(node);}

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

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

相关文章

xlua游戏热更新(lua访问C#)

CS.UnityEngine静态方法访问unity虚拟机 创建游戏物体 CS.UnityEngine.GameObject(new by lua);静态属性 CS.UnityEngine.GameObject(new by lua); -- 创建 local camera CS.UnityEngine.GameObject.Find(Main Camera); --查找 camera.name Renamed by Lua;访问组件 loca…

Swift--量值与基本数据类型

系列文章目录 第一章: Swift–量值与基本数据类型 文章目录 系列文章目录前言对学习过程做一个记录 变量和常量命名规范注释 元祖类型可选类型拆包 typealias 前言 对学习过程做一个记录 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 变量和常量 …

分享一下关于“vcruntime140_1.dll丢失的5种解决方法

今天我来给大家分享一下关于“vcruntime140_1.dll丢失的5种修复方法”的分享。首先&#xff0c;我们来了解一下vcruntime140_1.dll丢失的原因。 病毒感染&#xff1a;病毒或恶意软件可能损坏或删除vcruntime140_1.dll文件。 系统更新或软件安装&#xff1a;在进行系统更新或安…

vue+iView实现下载zip文件导出多个excel表格

1&#xff0c;需求&#xff1a;在vue项目中&#xff0c;实现分月份导出多个Excel表格。 点击导出&#xff0c;下载zip文件&#xff0c;解压出多张表数据。 2&#xff0c;关键代码&#xff1a; <Button class"export button-style button-space" click"ex…

【Bug】当用opencv库的imread()函数读取图像,用matplotlib库的plt.imshow()函数显示图像时,图像色彩出现偏差问题的解决方法

一&#xff0c;问题描述 我们在利用opencv的imread读取本地图像&#xff0c;进行一系列处理&#xff0c;但是发现用matplotlib库的imshow&#xff08;&#xff09;函数显示的时候出现色彩改变&#xff0c;比如图像偏黄&#xff0c;偏红&#xff0c;偏蓝等等&#xff0c;但是对…

智慧城市建设解决方案分享【完整】

文章目录 第1章 前言第2章 智慧城市建设的背景2.1 智慧城市的发展现状2.2 智慧城市的发展趋势 第3章 智慧城市“十二五”规划要点3.1 国民经济和社会发展“十二五”规划要点3.2 “十二五”信息化发展规划要点 第4章 大数据&#xff1a;智慧城市的智慧引擎4.1 大数据技术—智慧城…

【Python大数据笔记_day06_Hive】

hive内外表操作 建表语法 create [external] table [if not exists] 表名(字段名 字段类型 , 字段名 字段类型 , ... ) [partitioned by (分区字段名 分区字段类型)] # 分区表固定格式 [clustered by (分桶字段名) into 桶个数 buckets] # 分桶表固定格式 注意: 可以排序[so…

【算法与数据结构】131、LeetCode分割回文串

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;本题仍然使用回溯算法的一般结构。加入了一个判断是否是回文串的函数&#xff0c;利用起始和终止索引进…

阿里云-maven私服idea访问私服与组件上传

1.进入aliyun制品仓库 2. 点击 生产库-release进入 根据以上步骤修改本地 m2/setting.xml文件 3.pom.xml文件 点击设置获取url 4. idea发布组件

Flutter:改变手机状态栏颜色,与appBar状态颜色抱持一致

前言 最近在搞app的开发&#xff0c;本来没怎么注意appBar与手机状态栏颜色的问题。但是朋友一说才注意到这两种的颜色是不一样的。 我的app 京东 qq音乐 这样一对比发现是有的丑啊&#xff0c;那么如何实现呢&#xff1f; 实现 怎么说呢&#xff0c;真不会。百度到的一些是…

能谈一下 CAS 机制吗

&#xff08;本文摘自mic老师面试文档&#xff09; 一个小伙伴私信我&#xff0c;他说遇到了一个关于 CAS 机制的问题&#xff0c;他以为面试官问的是 CAS 实现单点登录。 心想&#xff0c;这个问题我熟啊&#xff0c;然后就按照单点登录的思路去回答&#xff0c;结果面试官一…

【算法练习Day46】判断子序列不同的子序列

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 判断子序列不同的子序列总结…

Linux文件缓冲区

文章目录 1. 缓冲区现象2. 用户级和系统级缓冲区3. 缓冲区刷新4. 为什么要有缓冲区5. 文件打印的全缓冲6. 模拟实现C语言文件标准库 本章gitee代码仓库&#xff1a;重定向、模拟C语言文件标准库 1. 缓冲区现象 我们这里分别调用了4个差不多的函数&#xff0c;但是结果是有一定差…

Spring面试题:(五)Spring注解开发@Component,@Autowired,@Bean,@Configuration

Bean基本注解 spring提供注解的版本 Component注解替代bean标签 bean其它属性的相关注解&#xff1a; scope 替代scopelazy 替代lazy-initPostConstruct 替代init-methodPreDestroy 替代destroy-method 使用Component注解的前提是开启注解扫描 衍生注解Repository,Servi…

水果音乐编曲软件 FL Studio v21.1.1.3750 中文免费破解版下载(附中文设置教程)

FL studio21中文别名水果编曲软件&#xff0c;是一款全能的音乐制作软件&#xff0c;包括编曲、录音、剪辑和混音等诸多功能&#xff0c;让你的电脑编程一个全能的录音室&#xff0c;它为您提供了一个集成的开发环境&#xff0c;使用起来非常简单有效&#xff0c;您的工作会变得…

Nginx:不同域名访问同一台机器的不同项目

Nginx很简单就可以解决同一台机器同时跑两个或者多个项目&#xff0c;而且都通过域名从80端口走。 以Windows环境下nginx服务为例&#xff0c;配置文件nginx.conf中&#xff0c;http中加上 include /setup/nginx-1.20.1/conf/conf.d/*.conf;删除server部分&#xff0c;完整如…

Nginx:如何实现一个域名访问多个项目

1. 背景介绍 最近在多个项目部署中遇到这样一个问题&#xff0c;一个域名如何实现多个项目的访问。因为不想自己单独去申请域名证书和域名配置&#xff0c;便想到了这个方案&#xff0c;结合Nginx的location功能实现了自己的需求&#xff0c;便记录下来。示例中是以项目演示&a…

Unity中Shader雾效的原理

文章目录 前言一、我们先看一下现实中的雾二、雾效的混合公式最终的颜色 lerp(雾效颜色&#xff0c;物体颜色&#xff0c;雾效混合因子) 三、雾效的衰减1、FOG_LINEAR&#xff08;线性雾衰减&#xff09;2、FOG_EXP(指数雾衰减1)3、FOG_EXP(指数雾衰减2) 前言 Unity中Shader雾…

Leetcode100128. 高访问员工

Every day a Leetcode 题目来源&#xff1a;100128. 高访问员工 解法1&#xff1a;模拟 把名字相同的员工对应的访问时间&#xff08;转成分钟数&#xff09;分到同一组中。 对于每一组的访问时间 accessTime&#xff0c;排序后&#xff0c;判断是否有 accessTime[i] - ac…