【数据结构—单链表的实现】

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

目录

前言

1. 链表的概念及结构

2. 单链表的实现

2.1单链表头文件——功能函数的定义

2.2单链表源文件——功能函数的实现

2.3 单链表源文件——功能的测试

3.具体的理解操作图

4. 链表的分类

总结


前言

世上有两种耀眼的光芒,一种是正在升起的太阳,一种是正在努力学习编程的你!一个爱学编程的人。各位看官,我衷心的希望这篇博客能对你们有所帮助,同时也希望各位看官能对我的文章给与点评,希望我们能够携手共同促进进步,在编程的道路上越走越远!


提示:以下是本篇文章正文内容,下面案例可供参考

1. 链表的概念及结构

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

链表的结构跟火车车厢相似,淡季时车次的车厢会相应减少,旺季时车次的车厢会额外增加几节。只需要将火车里的某节车厢去掉/加上,不会影响其他车厢,每节车厢都是独立存在的。

车厢是独立存在的,且每节车厢都有车门。想象一下这样的场景,假设每节车厢的车门都是锁上的状态,需要不同的钥匙才能解锁,每次只能携带一把钥匙的情况下如何从车头走到车尾?

最简单的做法:每节车厢里都放一把下一节车厢的钥匙。

在链表里,每节“车厢”是什么样的呢?

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

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

图中指针变量 plist保存的是第一个节点的地址,我们称plist此时“指向”第一个节点,如果我们希望plist“指向”第二个节点时,只需要修改plist保存的内容为0x0012FFA0。

为什么还需要指针变量来保存下一个节点的位置?

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

结合前面学到的结构体知识,我们可以给出每个节点对应的结构体代码:

假设当前保存的节点为整型:

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

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

当我们想要从第一个节点走到最后一个节点时,只需要在前一个节点拿上下一个节点的地址(下一个节点的钥匙)就可以了。

给定的链表结构中,如何实现节点从头到尾的打印?

思考:当我们想保存的数据类型为字符型、浮点型或者其他自定义的类型时,该如何修改?

补充说明:

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

2、节点一般是从堆上申请的

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

顺序表的缺点:

1:顺序表需要申请的空间是连续的,可能造成程序的消耗;

2:扩容存在一定的空间浪费。

2. 单链表的实现

2.1单链表头文件——功能函数的定义

Slist.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>//定义链表节点的结构:
typedef int SLDatatype;
typedef struct SlistNode
{SLDatatype data;//这个节点(结构体)内的数据struct SlistNode* next;//存放的是下一个节点(结构体)的地址
}SLNode;//我们来创建几个节点组成一个链表,并打印链表
//phead:第一个节点的地址
void SLprint(SLNode* phead);//尾插
void SLPushBack(SLNode** pphead, SLDatatype x);
//头插
void SLPushFront(SLNode** pphead, SLDatatype x);
//尾删
void SLPopBack(SLNode** pphead);
//头删
void SLPopFront(SLNode** pphead);//找节点,这里的第一个参数是一级指针还是二级指针
//这里穿一级指针实际就可以了,因为不改变节点
//但是这里要写二级指针,因为要保持接口的一致性
SLNode* SLFind(SLNode** pphead, SLDatatype x);//在指定位置之前插入数据
void SLInsert(SLNode** pphead, SLNode* pos, SLDatatype x);
//在指定位置之后插入数据
void SLInsertAfter(SLNode* pos, SLDatatype x);//删除pos节点
void SLErase(SLNode** pphead, SLNode* pos);
//删除pos节点之后
void SLEraseAfter(SLNode* pos);//链表的销毁
void SLDesTroy(SLNode** pphead);

2.2单链表源文件——功能函数的实现

Slist.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "Slist.h"
//顺序表在逻辑上是线性的,在物理空间上也是线性的,顺序表中的数据都是连续的
//链表在逻辑结构上是线性的,在物理空间上不是线性的,链表中的数据不是连续的
//一个节点中存放两个东西:1:存储的数据;2:下一个节点的地址链表节点的结构:
//struct SlistNode
//{
//	int data;//这个节点(结构体)内的数据
//	struct SlistNode* next;//存放的是下一个节点(结构体)的地址
//};void SLprint(SLNode* phead)//phead:是形参,指向第一个节点的地址
{//循环打印//可以使用phead直接来访问,但是注意,当代码走到//第27行的时候,此时的phead就是NULL了,//若代码没有写完,我们还要继续使用指向第一个节点的地址时,//这时我们就找不到第一个节点的地址了SLNode* pcur = phead;//申请一个变量,用来存放第一个节点的地址while (pcur != NULL){printf("%d ->", pcur->data);pcur = pcur->next;//pcur:是第一个节点的地址,在节点中找到下一个节点的地址,赋值给pcur}printf("NULL\n");
}//申请新节点插入数据
SLNode* SLBuyNode(SLDatatype x)
{SLNode* node = (SLNode*)malloc(sizeof(SLNode));node->data = x;node->next = NULL;return node;
}
//尾插
void SLPushBack(SLNode** pphead, SLDatatype x)
{//对指针加以限制assert(pphead);//传过来的指针不能为空SLNode* node = SLBuyNode(x);if (*pphead == NULL){*pphead = node;return;}//当链表不为空时,找尾SLNode* pcur = *pphead;while (pcur->next){pcur = pcur->next;}pcur->next = node;
}//头插
void SLPushFront(SLNode** pphead, SLDatatype x)
{assert(pphead);//链表为空和不为空的代码是一样的SLNode* node = SLBuyNode(x);//让新节点跟头节点连接起来node->next = *pphead;//*pphead:第一个节点的地址//让新的节点成为头节点*pphead = node;
}//尾删
void SLPopBack(SLNode** pphead)
{assert(pphead);//第一个节点不能为空(链表为空,不能尾删)assert(*pphead);//只有一个节点的情况下if ((*pphead)->next == NULL){//直接把头节点删除free(*pphead);*pphead = NULL;}else{//有多个节点的情况下//找到尾节点和尾节点的前一个节点SLNode* prev = NULL;SLNode* ptail = *pphead;//第一个节点的地址while (ptail->next != NULL){prev = ptail;ptail = ptail->next;//当ptail->next == NULL的时候,prev刚好是尾节点的前一个节点}//prev的next指针不在指向ptail,而是指向ptail的下一个节点prev->next = ptail->next;//next指向NULLfree(ptail);//为什么必须要置为空?代码后面明明没有在使用ptail?ptail = NULL;//因为打印的时候会判断地址是否为空,如果不置为空,那么会造成非法访问}
}
//头删
void SLPopFront(SLNode** pphead)
{assert(pphead);assert(*pphead);//无论是一个节点或者是多个节点都是可以的SLNode* del = *pphead;//第一个节点的地址*pphead = (*pphead)->next;//->的优先级高于*free(del);del = NULL;//出于代码规范的写法
}//查找第一个为x的节点
SLNode* SLFind(SLNode** pphead, SLDatatype x)
{assert(pphead);SLNode* pcur = *pphead;while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}//在指定位置之前插入数据
void SLInsert(SLNode** pphead, SLNode* pos, SLDatatype x)
{assert(pphead);//我们约定链表不能为空,pos也不能为空(链表为空,pos也绝对为空)assert(*pphead);assert(pos);SLNode* node = SLBuyNode(x);//只有一个节点的情况下(pos必须指向第一个节点的位置)//pos刚好是头节点,头插if ((*pphead)->next == NULL || pos == *pphead){node->next = *pphead;*pphead = node;return;}//找pos的前一个节点SLNode* prev = *pphead;while (prev->next != pos)//第一次循环的时候(缺少pos刚好是第一个节点的位置){prev = prev->next;}prev->next = node;node->next = pos;
}
//在指定位置之后插入数据
void SLInsertAfter(SLNode* pos, SLDatatype x)
{assert(pos);SLNode* node = SLBuyNode(x);node->next = pos->next;pos->next = node;
}//删除pos节点
void SLErase(SLNode** pphead, SLNode* pos)
{assert(pphead);assert(*pphead);assert(pos);//pos刚好是头节点if (pos == *pphead){*pphead = (*pphead)->next;free(pos);pos = NULL;return;}//当pos不是头节点,找pos的前一个节点SLNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;//只是规范
}
//删除pos节点之后
void SLEraseAfter(SLNode* pos)
{//pos节点不能为空,pos之后的节点不能为空assert(pos && pos->next);SLNode* del = pos->next;pos->next = del->next;free(del);del = NULL;
}//链表的销毁
void SLDesTroy(SLNode** pphead)
{assert(pphead);SLNode* pcur = *pphead;//循环删除while (pcur){SLNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

2.3 单链表源文件——功能的测试

test.c

#define _CRT_SECURE_NO_WARNINGS 1#include "Slist.h"void slttest()
{SLNode* node1 = (SLNode*)malloc(sizeof(SLNode));node1->data = 1;SLNode* node2 = (SLNode*)malloc(sizeof(SLNode));node2->data = 2;SLNode* node3 = (SLNode*)malloc(sizeof(SLNode));node3->data = 3;SLNode* node4 = (SLNode*)malloc(sizeof(SLNode));node4->data = 4;node1->next = node2;node2->next = node3;node3->next = node4;node4->next = NULL;//打印链表SLNode* plist = node1;SLprint(plist);
}
//检查一下写的代码是否正确
void slttest01()
{//plist:是第一个节点的地址SLNode* plist = NULL;//尾插SLPushBack(&plist, 1);SLPushBack(&plist, 2);SLPushBack(&plist, 3);SLPushBack(&plist, 4);SLprint(plist);//尾删//SLPopBack(&plist);//SLPopBack(&plist);//SLPopBack(&plist);//SLPopBack(&plist);//SLprint(plist);头删//SLPopFront(&plist);//SLPopFront(&plist);//SLPopFront(&plist);//SLPopFront(&plist);//SLNode* find = SLFind(&plist, 2);//SLInsert(&plist, find, 11);//SLInsertAfter(find, 100);SLDesTroy(&plist);SLprint(plist);
}int main()
{//slttest();slttest01();return 0;
}

3.具体的理解操作图

4. 链表的分类

链表的结构非常多样,以下情况组合起来就有8种(2 x 2 x 2)链表结构:

虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:

单链表 和 双向带头循环链表。

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

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


总结

好了,本篇博客到这里就结束了,如果有更好的观点,请及时留言,我会认真观看并学习。
不积硅步,无以至千里;不积小流,无以成江海。

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

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

相关文章

二叉树OJ题讲解之一

今天我们一起来做一道初级的二叉树OJ题&#xff0c;都是用递归思想解答 力扣965.单值二叉树 链接https://leetcode.cn/problems/univalued-binary-tree/description/ 所谓单值二叉树就是这棵二叉树的所有节点的值是相同的&#xff0c;那我们要做这道题&#xff0c;肯定要…

适用于iOS 的顶级苹果数据恢复软件

数据丢失可能随时发生在任何人身上&#xff0c;这可能是一种令人沮丧的经历。丢失 iOS 设备上的重要数据可能会造成特别严重的损失&#xff0c;因为其中可能包括有价值的照片、联系人、消息和其他重要文件。幸运的是&#xff0c;有多种数据恢复工具可以帮助用户恢复丢失的数据。…

Python基础:推导式(Comprehensions)详解

1. 推导式概念 Python推导式&#xff08;comprehensions&#xff09;是一种简洁而强大的语法&#xff0c;用于从已存在的数据&#xff08;列表、元组、集合、字典等&#xff09;中创建新的数据结构。推导式包括&#xff1a; 列表推导式元组推导式字典推导式集合推导式 2. 列表…

Vue 实现低代码开发平台,没想到这么好用!

前言 在众多开发技术中&#xff0c;Vue 组件化开发技术以其卓越的灵活性和高效性备受瞩目。 低代码平台相信不少人知道它的存在&#xff0c;而且现在大部分公司都在开发自己的低代码平台&#xff0c;首先我们来看看低代码平台可视化界面&#xff1a; 官网&#xff1a;https:/…

跨标签页通信的8种方式(下)

跨标签页通信是指在浏览器中的不同标签页之间进行数据传递和通信的过程。在传统的Web开发中&#xff0c;每个标签页都是相互独立的&#xff0c;无法直接共享数据。然而&#xff0c;有时候我们需要在不同的标签页之间进行数据共享或者实现一些协同操作&#xff0c;这就需要使用跨…

1233:单词倒置(C语言)

题目描述 最近birdfly收到了女友的几份信件&#xff0c;为了只要他俩知道信件的秘密&#xff0c;女友把信件里的每个单词都倒置了。这样只有birdfly将它们倒置过来才能明白女友的心思了。为此birdfly还特意请你编写程序帮他解决一下这个问题。 简单起见假定每封信只包含英文单词…

DDD全网最通俗易懂讲解(一)

基础概念 领域 领域就是用来确定范围的&#xff0c;范围即边界&#xff0c;这也是DDD在设计中不断强调边界的原因。简言之&#xff0c;DDD的领域就是这个边界内要解决的业务问题域。领域可以进一步划分为子领域。一个领域相当于一个问题域&#xff0c;领域拆分为子域的过程就…

详解二叉树

【本节目标】 1.树的概念和结构 2.二叉树的概念和结构 3.二叉树的顺序结构及实现 4.二叉树的链式结构及实现 1.树的概念及结构 1.1树的概念 树是一种非线性的数据结构&#xff0c;它由一个根结点和n(>0)个子树构成&#xff0c;之所以叫做树&#xff0c;是因为它很像生活…

Hive数据库与表操作

文章目录 一、准备工作二、Hive数据库操作&#xff08;一&#xff09;Hive数据存储&#xff08;二&#xff09;创建数据库&#xff08;三&#xff09;查看数据库&#xff08;四&#xff09;修改数据库信息 一、准备工作 二、Hive数据库操作 &#xff08;一&#xff09;Hive数据…

Python Selenium 图片资源自动搜索保存 项目实践

实现访问首页 from os.path import dirnamefrom selenium import webdriverclass ImageAutoSearchAndSave:"""图片自动搜索保存"""def __init__(self):"""初始化"""self.driver webdriver.Chrome(executable_pa…

西南科技大学数字电子技术实验二(SSI逻辑器件设计组合逻辑电路及FPGA实现 )FPGA部分

一、实验目的 1、掌握用SSI(小规模集成电路)逻辑器件设计组合电路的方法。 2、掌握组合逻辑电路的调试方法。 3、学会分析和解决实验中遇到的问题。 4、学会用FPGA实现本实验内容。 二、实验原理 包括:原理图绘制和实验原理简述 1、1位半加器 2、1位全加器 3、三…

2021年全国硕士研究生入学统一考试管理类专业学位联考英语(二)试题

文章目录 2021年全国硕士研究生入学招生考试英语二试题SectionⅠUse of EnglishSection Ⅱ Reading ComprehensionText 1Text 2Text 2Text 3Text 4 Section III TranslationSection Ⅳ Writing 2021年全国硕士研究生入学招生考试英语二试题 SectionⅠUse of English Directio…

oracle数据库备份2(expdp)

使用exp命令定时进行数据库备份的操作前面已经记录过&#xff1a; oralce数据库定时备份 下面记录下使用更加高效的expdp命令和impdp&#xff0c;这两个命令同样是用来做数据库备份和还原的&#xff0c;但速度更快&#xff0c;效率更高&#xff0c;缺点是只能用在服务器端进行…

阿里云ACE认证之国际版与国内版对比!

大厂疯狂裁员&#xff0c;互联网行业迎来寒冬&#xff0c;技术人员被动陷入疯狂内卷。在愈加内卷的IT领域&#xff0c;“云计算”作为少有的蓝海&#xff0c;无疑是打工人未来实现职场提升、摆脱内卷的绝佳选择&#xff01; 对于云计算行业的人来说&#xff0c;最值得考的肯定是…

洪泽湖流域建筑物、人口密度与土地利用数据技术服务

一&#xff0e;背景介绍 人类社会发展离不开土地&#xff0c;没有土地就没有人类&#xff0c;土地利用随着人类的出现而发生。人类为了一定的社会或经济方面的目的&#xff0c;会通过利用、改造等活动。从土地上获得更多的资源。土地利用既要受自然条件的制约&#xff0c;同时也…

2023年国赛试题:配置inux1 为 CA 服务器

试题内容:配置 linux1 为 CA 服务器,为 linux 主机颁发证书。证书颁发机构有 效期 10 年,公用名为 linux1.skills.lan。申请并颁发一张供 linux 服务器使用的证书,证书信息:有效期 =5 年,公用名=skills.lan, 国家=CN,省=Beijing,城市=Beijing,组织=skills,组织单位…

Unity UGUI的自动布局-LayoutGroup(水平布局)组件

Horizontal Layout Group | Unity UI | 1.0.0 1. 什么是HorizontalLayoutGroup组件&#xff1f; HorizontalLayoutGroup是Unity UGUI中的一种布局组件&#xff0c;用于在水平方向上对子物体进行排列和布局。它可以根据一定的规则自动调整子物体的位置和大小&#xff0c;使它们…

亚马逊云科技实现了奇瑞捷豹路虎SAP系统的上云目标并保持成本优化

11月23日&#xff0c;“2023第八届IDC中国数字化转型年度盛典”正式开启并揭晓“2023 IDC中国未来企业大奖-卓越奖”获奖企业&#xff0c;奇瑞捷豹路虎汽车有限公司&#xff08;以下简称“奇瑞捷豹路虎”&#xff09;凭借“基于云原生的智慧化运营平台”项目&#xff0c;继获得…

自动驾驶HWP功能规范

HWP功能规范 Highway Pilot Functional Specification 文件状态&#xff1a; 【√】草稿 【】正式发布 【】正在修改 文件起草分工 撰写&#xff1a; 审核&#xff1a; 编制&#xff1a; 签名&#xff1a; 日期&#xff1a; 审核&#xff1a; 签名&#xff1a; 日期&am…

企业业务场景如何实现自动化连接?

为什么要实现企业业务场景的自动化连接&#xff1f; 可提高效率&#xff0c;自动化连接可以减少人工操作和手动干预的需求&#xff0c;从而提高业务处理的速度和效率。通过自动化连接&#xff0c;不同的系统、应用程序和流程可以自动协同工作&#xff0c;减少了人工处理的时间和…