数据结构之单链表的详细实现(图解)

前言

本次博客讲结合图例讲解单向不带头非循环链表

此后会讲解一些题目

1单链表的实现

1.1什么是单链表

我们先看数组,即顺序表的是什么样的,再看链表

1.2单链表的特点

实际中要实现的链表的结构非常多样,以下情况组合起来就有8种链表结构:

1. 单向、双向

2. 带头、不带头

3. 循环、非循环

我们今天可以讲解最复杂的情况单向不带头非循环链表

1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结 构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都 是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带 来很多优势,实现反而简单了,后面我们代码实现了就知道了

链表的本质就是牺牲空间节省时间,它的访问速度快

1.2链表的实现

头文件部分
#pragma once
#define _SECURE_NO_WARNINGS 
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int datatype;
struct slnode{datatype data;struct slnode* next;
};
void slinit(struct slnode**pphead);
void slprint(struct slnode* phead);
void slPushBack(struct slnode** pphead,datatype put);
void slPushfront(struct slnode** pphead, datatype put);
void slPopfront(struct slnode** pphead);
void slPopback(struct slnode** pphead);
struct slnode* slFind(struct slnode* phead,datatype n);
void slInsertfront(struct slnode**pphead, datatype find,datatype i);
void slErase(struct slnode** pphead, datatype find);

大家简略看看就好,后面还会再提到,主要关注链表的定义

接下来讲解如何实现该链表

 初始化链表 slinit()

由于 没有哨兵位指针,所以它的初始化只需要让头指针为空即可

void slnodeinit(struct slnode **pphead){*pphead=NULL;}
扩展空间  struct slnode* buyslnode(datatype put)

如果要尾插或者头插一个链表,我们必须要动态开辟空间,才能增添一个结点

思路

首先他它有开辟一个空间,就必须要有一个指针记住该空间的地址

所以让该函数返回一个指针,注意在开辟成功后,必须要

让开辟的空间的next成员指向空指针,不然它就是一个野指针

struct slnode* buyslnode(datatype put)
{struct slnode* newnode = (struct slnode*)malloc(sizeof(struct slnode));if (newnode == NULL){return NULL;}newnode->data = put;//插入元素的大小newnode->next = NULL;//赋值w为空指针return newnode;
}
 头插void slPushfront(struct slnode** pphead, datatype put)

思路

如果一开始没有一个结点,也就是 phead此时为NULL

要让  *phead=buyslnode(put);

否则就是普通的头插,此时画图让大家理解头插

但此时 不管头结点是不是NULL指针都可以直接通过以下代码实现头插

void slPushfront(struct slnode** pphead, datatype put)
{struct slnode* newnode = buyslnode(put);newnode->next = *pphead;*pphead = newnode;
}
尾插void slPushBack(struct slnode** pphead,datatype put)

思路

仍然考虑phead为NULL的情况,此时只要让newnode=*pphead即可

但是它是需要找到尾巴也就是最后一个非0结点

怎么找,看图

void slPushBack(struct slnode** pphead,datatype put)
{struct slnode* newnode = buyslnode(put);if (*pphead == NULL){*pphead = newnode;}else{struct slnode* cur = *pphead;while (cur->next){cur = cur->next;}cur->next = newnode;}
}
打印 void slprint(struct slnode* phead)

其实只需要遍历一遍即可

直接看代码

但是要注意这里是cur!=NULL才算遍历完

void slprint(struct slnode* phead)
{struct slnode* cur = phead;while (cur){printf("%d->", cur->data);cur = cur->next;}printf("NULL\n");
}

到这里应该要测试一遍了

看图

这里分别尾插1 2 3 4 5 最后头插一个 0 再把他们打印出来

尾删 void slPopback(struct slnode** pphead)

分析

首先要有结点才能删除,所以头结点可以通过assert判断

如果只有一个结点让头指针置空

如果有两个以上的结点那么就找到尾巴并且找到尾巴前一个结点让它置空,并删除尾结点

看图吧

看代码

void slPopback(struct slnode** pphead)
{assert(*pphead);struct slnode* cur = *pphead;struct slnode* prev = *pphead;if (cur->next == NULL){free(*pphead);pphead = NULL;}else{while (cur->next){prev = cur;cur = cur->next;}free(cur);prev->next = NULL;}}

头删 void slPopfront(struct slnode** pphead)

分析

首先还是要有结点可删才可以删,所以还是要assert

其次还是这样,如果只有一个结点那就直接free掉

如果有两个以上的结点,还要找到下一个结点

看图

这里头删只要一个变量就可

void slPopfront(struct slnode** pphead)
{assert(*pphead);if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{struct slnode*tem = (*pphead)->next;free(*pphead);*pphead = tem;}
}
 定位 struct slnode* slFind(struct slnode* phead, datatype n)

分析

遍历一遍 判断即可

struct slnode* slFind(struct slnode* phead, datatype n)
{assert(phead);while (phead){if (phead->data== n){return phead;}elsephead = phead->next;}printf("没有该数据\n");return NULL;
}
 在pos前插入 void slInsertfront(struct slnode**pphead, datatype find, datatype i)

分析

先找到该位置,如果没有找到位置,结束

找到该位置后

如果该位置是第一个位置就是头插

还得找到这个位置的前一个位置把他们相连

看图

void slInsertfront(struct slnode**pphead, datatype find, datatype i)
{struct slnode* pfind=slFind(*pphead, find);if (pfind == NULL)return;else{if (pfind == *pphead)slPushfront(pphead, i);else{struct slnode* cur = *pphead;while (cur->next != pfind){cur = cur->next;}cur->next = buyslnode(i);cur->next->next = pfind;}}
}
删除该位置的数据 void slErase(struct slnode** pphead, datatype find)

分析

但凡要删除数据就必须要有数据,所以还是要assert一下头指针

如果是pos位置在头指针位置即为头删 如果是在最后一个结点即为尾删

如果在中间,就是必须要找到前一个和后一个结点

看图

看代码

void slErase(struct slnode** pphead, datatype find)
{struct slnode* pfind = slFind(*pphead, find);if (pfind == *pphead){*pphead = pfind->next;free(pfind);}else if(pfind->next==NULL){slPopback(pphead);}else{struct slnode* cur = *pphead;while (cur->next != pfind){cur = cur->next;}cur->next = pfind->next;free(pfind);}
}

至此基本所有的功能都实现了

我们可以测试一下

看看首先 尾插 1 2 3 4 5然后头插一个0

此时链表为 0 1 2 3 4 5 

然后再头删 尾删 链表为  1 2 3 4

再在2的位置前插入10,删除4这个结点

最终结果为 1 2 10 3      ok对上了

总结

到这里单链表的实现就完成了,还是多练

祝大家开心

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

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

相关文章

HarmonyOS实战开发-为应用添加运行时权限

介绍 通过AbilityAccessCtrl动态向用户申请“允许不同设备间的数据交换”的权限&#xff0c;使用设备管理实例获取周边不可信设备列表。 说明&#xff1a; 查询周边不可信设备之前&#xff0c;请确保本设备与周边设备未进行配对。如果已配对&#xff0c;则恢复出厂设置之后重新…

树与二叉树的应用试题解析

01&#xff0e;在有n个叶结点的哈夫曼树中&#xff0c;非叶结点的总数是( A ). A. n-1 B. n C. 2n-1 D.2n 02.给定整数集合{3,5,6,9,12}&#xff0c;与之对应的哈夫曼树是( D…

MySQL - 高阶语句(二)

目录 6. 子查询 操作&#xff1a; EXISTS 关键字 别名 as 7. 视图 理论&#xff1a; 操作&#xff1a; 视图的优缺点 8. NULL 值 9. union 联级 9.1 union &#xff08;合并后去重&#xff09; 9.2 union all (合并后不去重) 9.3 取非交集值 10. case 条件选择查…

Vue 与 React:前端框架对比分析

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

MySQL数据库(MySQL主从搭建|Django中实现MySQL读写分离|Django中使用MySQL连接池)

文章目录 一、MySQL主从搭建1.MySQL主从的目的&#xff1f;2.MySQL主从原理3.搭建步骤 二、Django中实现MySQL读写分离1.使用sqlite实现读写分离2.MySQL实现读写分离 三、Django中使用连接池1.使用池的目的2.Django中使用MySQL连接池 一、MySQL主从搭建 1.MySQL主从的目的&…

游戏开发笔记:游戏海外版本时区问题(解释时区问题,分解为js写法和lua写法来分析记录,整理出对应语言的相关函数方法。)

对于海外游戏而言,与时间相关的功能,都不能忽略时区的计算。根据 ‘ 服务端资源是有限的,客户端资源是无穷无尽的 ’的定义来说,基本上时区包括时间的计算都是由客户端来进行计算,今天内容也是围绕客户端来展开。 时区算法常见的时间描述时区需要计算的点在lua语言中的写…

鸿蒙OS开发问题:(ArkTS)【 RSA加解密,解决中文乱码等现象】

RSA加解密开始构建工具类就是举步维艰&#xff0c;官方文档虽然很全&#xff0c;但是还是有很多小瑕疵&#xff0c;在自己经过几天的时间&#xff0c;彻底解决了中文乱码的问题、分段加密的问题。 首先看官方示例代码(以RSA非对称加解密&#xff08;多次调用doFinal实现分段&a…

DevSecOps平台架构系列-互联网企业私有化DevSecOps平台典型架构

目录 一、概述 二、私有化DevSecOps平台建设思路 2.1 采用GitOps公有云建设 2.2 采用GitOps私有云建设 2.3 总结 三、GitOps及其生态组件 3.1 采用GitOps的好处 3.1.1 周边生态系统齐全 3.1.2 便于自动化的实现 3.1.3 开发人员属性GitOps 3.2 GitOps部分生态组件介绍…

苹果 WWDC 24 将举行;高通、谷歌、英特尔等联合开发 AI 软件;艺术家谈及使用 Sora 创作视频体验

▶ 苹果WWDC 24 将于当地时间 6 月 10 日召开 3 月 27 日凌晨&#xff0c;苹果官宣将于当地时间 6 月 10 日举行今年的全球开发者发布大会。 苹果全球营销高级副总裁 Greg Joswiak 在社交媒体上表示&#xff1a;「在您的日历标记上 WWDC24 吧。这场活动无疑会令人惊喜&#xf…

如何使用极狐GitLab 自定义 Pages 根域名

本文作者&#xff1a;徐晓伟 GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 本文主要讲述了极狐GitLab Pages …

前端埋点全解及埋点SDK实现方式

一、什么是埋点 所谓“埋点”&#xff0c;是数据采集领域&#xff08;尤其是用户行为数据采集领域&#xff09;的术语&#xff0c;指的是针对特定用户行为或事件进行捕获、处理和发送的相关技术及其实施过程。比如用户某个icon点击次数、观看某个视频的时长等等。 埋点…

春秋云境CVE-2023-1313

简介 cockpit在2.4.1版本之前存在任意文件上传漏洞PS&#xff1a;通过在浏览器中打开/install来运行安装 正文 来到靶场&#xff0c;首先进行弱口令爆破&#xff0c;发现没用&#xff0c;那么只好老老实实的看靶场提示 先来访问/install 访问后就可以进行登录了&#xff0c…