单链表原来是这样实现的!

news2024/11/29 19:51:30

文章目录

  • 前言
  • 1. 链表的概念及结构
    • 1.1在链表里,每节“车厢”是什么样的呢?
    • 1.2为什么还需要指针变量来保存下⼀个节点的位置?
  • 2. 单链表的实现
    • 1. 定义结构体`(Seqlist)`
    • 2. 打印函数`(SLTPrint)`
    • 小插曲,创建节点函数`CreateNode`
    • 3. 尾插函数 `(SLTPushBack)`
    • 4. 头插函数 `(SLTPushFront)`
    • 5. 尾删函数(`SLTPopBack`)
    • 6. 头删函数(`SLTPopFront`)
    • 小插曲,pos查找函数` SLTFind`
    • 7. “插入指定位置前”函数(`SLTInster`)
    • 8.“删除指定位置后”函数
    • 9.销毁单链表函数`SLTDestroy`
  • 结语

前言

“我会定期分享我的学习经验,也欢迎大家留言和交流,让我们共同学习和进步!感谢大家的支持,让我们一起开启这段充满技术乐趣的旅程吧!”


1. 链表的概念及结构

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

在这里插入图片描述

链表的结构跟火车车厢厢相似,淡季时车次的车厢会相应减少,旺季时车次的车厢会额外增加几节。只需要将火车里的某节节厢去掉/加上,不会影响其他车厢,每节车厢都是独立存在的。
车厢是独里存在的,且每节车厢都有车门。想象⼀下这样的场景,假设每节车厢的车门都是锁上的状态,需要不同的钥匙才能解锁,每次只能携带⼀把钥匙的情况下如何从车头走到车尾?
最简单的做法:每节车厢里都放⼀把下一节车厢的钥匙。

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

在这里插入图片描述

与顺序表不同的是,链表⾥的每节"车厢"都是独立申请下来的空间,我们称之为“结点/节点”,节点的组成主要有两个部分:当前节点要保存的数据和保存下一个节点的地址(指针变量)。
图中指针变量 plist保存的是第⼀个节点的地址,我们称plist此时“指向”第⼀个节点,如果我们希望plist“指向”第⼆个节点时,只需要修改plist保存的内容为0x0012FFA0。

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

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


2. 单链表的实现

1. 定义结构体(Seqlist)

在SList.h头文件中

typedef int SLNDataType;
typedef struct SListNode
{
  SLNDataType val;
  struct SList* next;
 //这里只是指针,不是结构体
}SLNode;

2. 打印函数(SLTPrint)

注意下述代码皆是:
SList.h头文件中定义函数
SList.c文件中实现函数
Test.c文件中函数测试

SeqList.h文件中
定义函数:
在这里插入图片描述

SList.c文件中
实现函数:

void SLTPrint(SLNode* phead) //打印单链表
{
  SLNode* cur = phead;
  while (cur != NULL)
  {
    printf("%d->", cur->val);
    cur=cur->next;
  }
  printf("NULL");
}

小插曲,创建节点函数CreateNode

在实现下面的插入函数之前,还需要一个函数来开辟空间给新的节。
函数实现

SLNode* CreateNode(SLNDataType x) //新建节点,开辟空间
{
  SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
  if (newnode == NULL)
  {
    perror("malloc fail");
    exit(-1);
  }
  newnode->val = x;
  newnode->next = NULL;
  return newnode;
}

3. 尾插函数 (SLTPushBack)

定义函数:

在这里插入图片描述

实现函数:

void SLTPushBack(SLNode** pphead, SLNDataType x) //尾插
{
  SLNode* newnode = CreateNode(x);
  if (* pphead == NULL)
  {
    *pphead = newnode;
  }
  else
  {
    SLNode* tail = * pphead; //找尾
    while (tail->next != NULL)
    {
      tail = tail->next;  //因为tail是局部变量,而tail->next是结构体,出来作用域tail就销毁了
    }
    tail->next = newnode; //所以这里把newnode赋值给tail->next
  }
}

函数测试:

int main()
{
  SLNode* plist = NULL;
  SLTPushBack(&plist, 1);
  SLTPushBack(&plist, 2);
  SLTPushBack(&plist, 3);
  SLTPrint(plist);
  return 0;
}

运行结果:
![在这里插入图片描述](https://img-blog.csdnimg.cn/4b96a69eca1243a1aa4a0e9ebf888f87.png


4. 头插函数 (SLTPushFront)

定义函数:

![在这里插入图片描述](https://img-blog.csdnimg.cn/6c514f963f144d65a681e2c56dad01ac.png

实现函数:

void SLTPushFront(SLNode** pphead, SLNDataType x) //头插
{ 
  SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
  newnode->next =* pphead;
  newnode->val = x;
  *pphead = newnode;
}

函数测试:

int main()
{
  SLNode* plist = NULL;
  SLTPushFront(&plist,520 );
  SLTPushBack(&plist,1);
  SLTPushBack(&plist,1);
  SLTPushFront(&plist,520);
  SLTPrint(plist);
  return 0;
}

运行结果:
在这里插入图片描述


5. 尾删函数(SLTPopBack)

定义函数:

在这里插入图片描述

实现函数:

void SLTPopBack(SLNode** pphead)  //尾删
{
  assert(pphead);
  assert(*pphead);
  if ((*pphead)->next== NULL)
  {
    free(*pphead);
    *pphead = NULL;
  }
  else
  {
     SLNode* prev = NULL;
     SLNode* tail = *pphead;
     while (tail->next != NULL)
     {
       prev = tail;
       tail = tail->next;
     }
     free(tail);
     tail = NULL;
     prev->next = NULL;
  }
}

函数测试:

int main()
{
  SLNode* plist = NULL;
  SLTPushFront(&plist,520 );
  SLTPushBack(&plist,1314);
  SLTPushBack(&plist,00544);
  SLTPopBack(&plist);
  SLTPrint(plist);
  return 0;
}

运行结果:
在这里插入图片描述


6. 头删函数(SLTPopFront)

定义函数:

![在这里插入图片描述](https://img-blog.csdnimg.cn/dff2c7abe0f7483d83f82307db1ceb3f.png

实现函数:

void SLTPopFront(SLNode** pphead) //头删
{
  assert(*pphead);
  SLNode* tail = *pphead;
  tail = tail->next;
  free(*pphead);
  *pphead = tail;
}

函数测试:

int main()
{
  SLNode* plist = NULL;
  SLTPushFront(&plist,5201314);
  SLTPushBack(&plist,00544);
  SLTPushBack(&plist,44944);
  SLTPopFront(&plist);
  SLTPrint(plist);
  return 0;
}

运行结果:
在这里插入图片描述


小插曲,pos查找函数 SLTFind

用来确定pos位置,方便后面调用
实现函数:

SLNode* SLTFind(SLNode** pphead, SLNDataType x) //pos的查找函数
{
  assert(pphead);
  SLNode* cur = *pphead;
  while (cur)
  {
    if (cur->val == x)
    {
      return cur;
    }
    cur = cur->next;
  }
  return NULL;
}

7. “插入指定位置前”函数(SLTInster)

定义函数:

![在这里插入图片描述](https://img-blog.csdnimg.cn/94333646a22546b4a4baf71c46711172.png

实现函数:

void* SLTInster(SLNode** pphead, SLNode* pos, SLNDataType x) //指定位置前面插入
{
  assert(pos);
  assert(pphead);
  assert(*pphead);
  SLNode* node = CreateNode(x);
  if (*pphead == pos)
  {
    node->next = *pphead;
    *pphead = node;
  }

  SLNode* cur = *pphead;
  while (cur->next != pos)
  {
    cur = cur->next;
  }
  cur->next = node;
  node->next = pos;
}

函数测试:

int main()
{
  SLNode* plist =NULL;
  SLTPushBack(&plist,1);
  SLTPushBack(&plist,2);
  SLTPushBack(&plist,3);
  SLNode* Find = SLTFind(&plist, 3);
  SLTInster(&plist,Find,123);
  SLTPrint(plist);
  return 0;
}

运行结果:
如同在第一个值为3的节点前面插入了123;
在这里插入图片描述


8.“删除指定位置后”函数

定义函数:

在这里插入图片描述

实现函数:

void* SLTEraseAfter(SLNode* pos) //指定位置后面删除
{
  assert(pos && pos->next);
  SLNode* del = pos->next;
  pos->next = del->next;
  free(del);
}

函数测试:

int main()
{
  SLNode* plist =NULL;
  SLTPushBack(&plist,520);
  SLTPushBack(&plist,2);
  SLTPushBack(&plist,520);
  SLNode* Find = SLTFind(&plist, 2);
  SLTEraseAfter(Find);
  SLTPrint(plist);
  return 0;
}

运行结果:
如图在第一个值为520的节点后面删除了小3;
在这里插入图片描述


9.销毁单链表函数SLTDestroy

定义函数:
在这里插入图片描述
实现函数:

void SLTDestroy(SLNode** pphead) //销毁单链表
{
  assert(pphead);
  SLNode* cur= *pphead;
  while (cur)
  {
    SLNode* next = cur;
    free(cur);
    cur = next;
  }
  *pphead = NULL;
}

测试函数:

int main()
{
  SLNode* plist =NULL;
  SLTPushBack(&plist,1);
  SLTPushBack(&plist,2);
  SLTPushBack(&plist,3);
  SLTDestroy(&plist);
  return 0;
}

结语

感谢您阅读我的博客,我希望您能从中获得一些启发和帮助。如果您喜欢这篇博客,请分享给您的朋友,也欢迎留下您的评论和反馈。您的支持是我继续分享和创作的动力。谢谢!希望我们能在未来的博客中再次相见。祝您一切顺利,期待与您再次相会!

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

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

相关文章

一种LED驱动专用控制电路

一、基本概述 TM1620是一种LED(发光二极管显示器)驱动控制专用IC,内部集成有MCU数字接口、数据锁存 器、LED驱动等电路。本产品质量可靠、稳定性好、抗干扰能力强。主要适用于家电设备(智能热 水器、微波炉、洗衣机、空调、电磁炉)、机顶盒、电子称、…

清分系统对账

流程1的问题: 1、通道一天的数据会有多少,有二三十万条交易数据吗? 2、如果数据过大都存到一个Map里面去,机器不得挂了 步骤1总结: 1、通过channelNo获取通道T的数据,因为通道是一天一个文件给过来。在转…

xxljob学习笔记01(小滴课堂)

分布式调度xxl-job源码部署和数据库建立: 在idea中打开安装包: 创建数据库: 建表: 在项目里: 在navicat里运行语句即可: 修改数据库地址和用户名,密码: 配置令牌,不然谁…

WiFi的CSMA/CA竞争窗口流程简述

1、若站点最初有数据要发送(不是发送不成功再进行重传的那种),且检测到信道空闲,在等待DIFS后,就发送整个数据帧。 2、否则,站点执行退避算法。一旦检测到信道忙,就冻结退避计时器。只要信道空…

卸载idea2017-2023步骤 (卸载干净)

1. 右击打开软件所在位置 2. 找到卸载程序 Uninstall.exe, 双击打开 3. 开始卸载 4. 注册表删除 打开winR, 输入命令regedit Ctrl F查找 "jetbrain" 删除查找出来的文件夹。卸载干净, 即可安装新idea

【Amazon】基于Amazon提供的托管式EKS通过eksctl命令部署Kubernetes集群

文章目录 一、使用CloudFormation创建堡垒机二、安装AWS CLI命令行工具三、安装eksctl命令行工具四、创建集群角色4.1 集群服务角色创建4.2 集群节点组角色创建 五、创建 EKS集群六、登录EKS控制台七、参考链接 一、使用CloudFormation创建堡垒机 导航至CloudFormation&#xf…

【前沿技术了解】web图形Canvas、svg、WebGL、数据可视化引擎的技术选型

目录 Canvas:HTML5新增 Canvas标签(画布) 渲染上下文canvas.getContext(contextType[, contextAttributes]) 上下文类型(contextType) 上下文属性 (contextAttributes) 示例 动画 setInterval(function, delay)…

【go入门】表单

4.1 处理表单的输入 先来看一个表单递交的例子&#xff0c;我们有如下的表单内容&#xff0c;命名成文件login.gtpl(放入当前新建项目的目录里面) <html> <head> <title></title> </head> <body> <form action"/login" meth…

软件介绍01- koodo Reader支持所有电脑平台!

1 软件简介 Koodo Reader软件是一款阅读器&#xff0c;可以阅读各种格式的文档。用来代替kindle。界面简洁&#xff0c;好看&#xff0c;阅读功能强大&#xff0c;而且可以多设备同步。 因为开源&#xff0c;所以免费。而且支持所有电脑平台&#xff01; 支持格式&#xff1a…

kafka2.x常用命令:创建topic,查看topic列表、分区、副本详情,删除topic,测试topic发送与消费

原创/朱季谦 接触kafka开发已经两年多&#xff0c;也看过关于kafka的一些书&#xff0c;但一直没有怎么对它做总结&#xff0c;借着最近正好在看《Apache Kafka实战》一书&#xff0c;同时自己又搭建了三台kafka服务器&#xff0c;正好可以做一些总结记录。 本文主要是记录如…

解密Kafka主题的分区策略:提升实时数据处理的关键

目录 一、Kafka主题的分区策略概述1.1 什么是Kafka主题的分区策略&#xff1f;1.2 为什么分区策略重要&#xff1f; 二、Kafka默认分区策略2.1 Round-Robin分区策略 三、自定义分区策略3.1 编写自定义分区器3.2 最佳实践&#xff1a;如何选择分区策略 四、分区策略的性能考量4.…

FO-like Transformation

参考文献&#xff1a; [RS91] Rackoff C, Simon D R. Non-interactive zero-knowledge proof of knowledge and chosen ciphertext attack[C]//Annual international cryptology conference. Berlin, Heidelberg: Springer Berlin Heidelberg, 1991: 433-444.[BR93] Bellare M…

粉丝提问:写博文怎样才能变现啊?

文章目录 粉丝提问&#xff1a;写博文怎样才能变现啊&#xff1f;我总结了一下博客变现的几个途径&#xff1a;另外做技术博主的五大好处 后记 粉丝提问&#xff1a;写博文怎样才能变现啊&#xff1f; type: Post status: Published date: 2023/11/26 tags: 推荐 category…

为什么PCB板大多数都是绿色的?

什么时候开始接触到PCB板的呢&#xff1f;也许是是把家里的电视遥控器拆开的时候&#xff0c;也许是你的小霸王学习机游戏手柄给按坏拆开的时候&#xff0c;也许那时候你还不知道这叫PCB电路板。然后就是大学里使用嘉立创免费打板的时候&#xff0c;有一个选项绿色板还是黑色板…

Java 文件常用操作与流转换

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f4ab; GitCode &#x1f496; 欢迎点赞…

监控同一局域网内其它主机上网访问信息

1.先取得网关IP 2.安装IPTABLES路由表 sudo apt-get install iptables 3.启用IP转发 sudo sysctl -p 查看配置是否生效 4.配置路由 iptables -t nat -A POSTROUTING -j MASQUERADE 配置成功后,使用sudo iptables-save查看

Linux进程通信——信号量

概念 信号量(semaphore) 与已经介绍过的 PC 结构不同&#xff0c;它是一个计数器。信号量用于实现进程间的互斥与同步&#xff0c;而不是用于存储进程间通信数据。 特点 1.信号量用于进程间同步&#xff0c;若要在进程间传递数据需要结合共享内存 2.信号量基于操作系统的 PV…

C语言线性表的链式存储(框架)

线性表的链式存储 线性表的顺序存储&#xff1a;用一块连续的内存空间 线性表的链式存储&#xff1a;不连续的内存空间 链表是由一系列的节点组成&#xff0c;每个节点包含两个域&#xff0c;一个是数据域&#xff0c;一个是指针域 链表的插入和删除原理 单项链表框架的搭建 …

交换机配置与管理

文档以国产迈普交换机为例&#xff0c;各厂家交换机配置有少许不同&#xff0c;仅供参考。 交换机命令行模式&#xff1a; 普通用户模式Hostname>&#xff08;&#xff09; exit 输入enable命令 特权用户模式Hostname#&#xff08;&#xff09; exit 输入configu…

业务流程图是什么,怎么画?

业务流程图是一种展示企业内部流程和工作流程的图表&#xff0c;通常以图表的形式呈现。业务流程图用图像化的方式展示组织内部的各种活动&#xff0c;每个操作环节被展示为流程图的一个框&#xff0c;一般包括输入/输出、任务和活动等元素。 业务流程图的使用场景 业务流程…