【数据结构】_C语言实现不带头非循环单向链表

目录

1. 链表的概念及结构

2. 链表的分类

3. 单链表的实现

3.1 SList.h头文件

3.2 SList.c源文件

3.3 Test_SList.c测试文件


关于线性表,已介绍顺序表,详见下文:

【数据结构】_顺序表-CSDN博客

本文介绍链表;

基于顺序表的特点,思考改善方案:

按需申请释放空间,不再将数据存储于连续的一整块空间中,而是需要一个数据开辟一个小空间。为了方便访问数据,首先创建一个头指针(头结点)指向存放第一个数据的内存位置处,而在该位置处,除了存储该数据本身,再分配一块空间用于存放下一个数据的地址,直至某位置存放的下一个位置的指针为空则数据截止。

同时,这种存储方式也有效地提高了插入删除数据时的效率,无需再大量挪动数据。

这种数据结构就称为链表。

1. 链表的概念及结构

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

链表在逻辑上是连续的,在物理上不是连续的

2. 链表的分类

对于链表,有单向和双向、带头与不带头、循环与不循环的分类,不同的组合如下;

各种类型的链表示意图如下:

单向和双向:(区别标准:能从一个/两个方向遍历)

带头和不带头:(是否在第一个有效结点前增加一个头结点)

循环和非循环:(尾结点的next为NULL/指向第一个结点)

最常用的链表结构是:无头单向非循环链表带头双向循环链表

注:头结点并不是第一个有效结点,而是在第一个有效结点前再创建一个不存储有效数据的结点

3. 单链表的实现

3.1 SList.h头文件

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>// 链表结点
typedef int SLTDataType;
typedef struct SListNode {SLTDataType data;struct SListNode* next;
}SLTNode;
void SLTPrint(SLTNode* phead);
void SLTPushBack(SLTNode** pphead, SLTDataType x);
void SLTPushFront(SLTNode** pphead, SLTDataType x);
void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);
SLTNode* SLTCreatNode(SLTDataType x);
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
// 在指定位置前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
// 在指定位置后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
// 删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos);
// 删除pos后的结点
void SLTEraseAfter(SLTNode* pos);
// 销毁
void SLTDestory(SLTNode** pphead);

3.2 SList.c源文件

#include"SList.h"
void SLTPrint(SLTNode* phead) {SLTNode* pcur = phead;while (pcur) {printf("%d-> ", pcur->data);pcur = pcur->next;}printf("NULL\n");
}
SLTNode* SLTCreatNode(SLTDataType x) {SLTNode* newNode = (SLTNode*)malloc(sizeof(SLTNode));if (newNode == NULL) {perror("malloc fail\n");exit(1);}newNode->data = x;newNode->next = NULL;return newNode;
}
void SLTPushBack(SLTNode** pphead, SLTDataType x) {assert(pphead);SLTNode* newNode = SLTCreatNode(x);// 空链表if (*pphead == NULL) {*pphead = newNode;}else {// 非空链表SLTNode* curNode = *pphead;while (curNode->next) {curNode = curNode->next;}curNode->next = newNode;}
}
void SLTPushFront(SLTNode** pphead, SLTDataType x) {assert(pphead);SLTNode* newNode = SLTCreatNode(x);newNode->next = *pphead;// 令新结点为链表的头结点*pphead = newNode;
}
void SLTPopBack(SLTNode** pphead) {assert(pphead && *pphead);// 链表只有一个结点if ((*pphead)->next == NULL) {free(*pphead);*pphead = NULL;}// 链表有多个结点else {SLTNode* tailPrevNode = *pphead;SLTNode* tailNode = *pphead;while (tailNode->next) {tailPrevNode = tailNode;tailNode = tailNode->next;}free(tailNode);tailNode = NULL;tailPrevNode->next = NULL;}
}
void SLTPopFront(SLTNode** pphead) {assert(pphead && *pphead);SLTNode* secNode = (*pphead)->next;free(*pphead);*pphead = secNode;
}
SLTNode* SLTFind(SLTNode* phead, SLTDataType x) {SLTNode* curNode = phead;while (curNode) {if (curNode->data == x) {return curNode;}curNode = curNode->next;}return NULL;
}
// 在指定位置前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x) {assert(pphead && *pphead && pos);SLTNode* newNode = SLTCreatNode(x);if (pos == *pphead) {// 调用头插方法SLTPushFront(pphead, x);}else {SLTNode* posPrevNode = *pphead;while (posPrevNode->next != pos) {posPrevNode = posPrevNode->next;}posPrevNode->next = newNode;newNode->next = pos;}
}
// 在指定位置后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x) {assert(pos);SLTNode* newNode = SLTCreatNode(x);SLTNode* posAfterNode = pos->next;pos->next = newNode;newNode->next = posAfterNode;
}
// 删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos) {assert(pphead && *pphead && pos);if (*pphead == pos) {// 调用头删方法SLTPopFront(pphead);}else {SLTNode* posPrevNode = *pphead;while (posPrevNode->next != pos) {posPrevNode = posPrevNode->next;}posPrevNode->next = pos->next;free(pos);pos = NULL;}
}
// 删除pos后的结点
void SLTEraseAfter(SLTNode* pos) {assert(pos && pos->next);SLTNode* posAfterNode = pos->next;pos->next = posAfterNode->next;free(posAfterNode);posAfterNode = NULL;
}
// 销毁
void SLTDestory(SLTNode** pphead) {assert(pphead && *pphead);SLTNode* curNode = *pphead;while (curNode) {SLTNode* curNextNode= curNode->next;free(curNode);curNode = NULL;}*pphead = NULL;
}

3.3 Test_SList.c测试文件

#include"SList.h"
void Test11() {SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);SLTDestory(&plist);SLTPrint(plist);
}
void Test10() {SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);SLTNode* aimNode1 = SLTFind(plist, 3);SLTEraseAfter(aimNode1);SLTPrint(plist);SLTNode* aimNode2 = SLTFind(plist, 1);SLTEraseAfter(aimNode2);SLTPrint(plist);
}
void Test9() {SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);SLTNode* aimNode1 = SLTFind(plist, 3);SLTErase(&plist, aimNode1);SLTPrint(plist);SLTNode* aimNode2 = SLTFind(plist, 1);SLTErase(&plist, aimNode2);SLTPrint(plist);
}
void Test8() {SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);SLTNode* aimNode1 = SLTFind(plist, 3);SLTInsertAfter(aimNode1, 85);SLTPrint(plist);SLTNode* aimNode2 = SLTFind(plist, 1);SLTInsertAfter(aimNode2, 97);SLTPrint(plist);
}
void Test7(){SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);SLTNode* aimNode1 = SLTFind(plist, 3);SLTInsert(&plist, aimNode1, 85);SLTPrint(plist);SLTNode* aimNode2 = SLTFind(plist, 1);SLTInsert(&plist, aimNode2, 97);SLTPrint(plist);
}
void Test6() {SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);// SLTNode* aimNode = SLTFind(plist, 3);SLTNode* aimNode = SLTFind(plist, 6);if (aimNode == NULL)printf("Find nothing\n");elseprintf("Find successfully\n");
}
void Test5() {SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPrint(plist);SLTPopFront(&plist);SLTPrint(plist);SLTPopFront(&plist);SLTPrint(plist);SLTPopFront(&plist);SLTPrint(plist);
}
void Test4() {SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPrint(plist);SLTPopBack(&plist);SLTPrint(plist);SLTPopBack(&plist);SLTPrint(plist);SLTPopBack(&plist);SLTPrint(plist);
}
void Test3() {SLTNode* plist = NULL;SLTPushFront(&plist, 1);SLTPushFront(&plist, 2);SLTPushFront(&plist, 3);SLTPushFront(&plist, 4);SLTPrint(plist);
}
void Test2() {SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);
}void Test1() {SLTNode* node1 = (SLTNode*)malloc(sizeof(SLTNode));node1->data = 1;SLTNode* node2 = (SLTNode*)malloc(sizeof(SLTNode));node2->data = 2;SLTNode* node3 = (SLTNode*)malloc(sizeof(SLTNode));node3->data = 3;SLTNode* node4 = (SLTNode*)malloc(sizeof(SLTNode));node4->data = 4;node1->next = node2;node2->next = node3;node3->next = node4;node4->next = NULL;SLTNode* plist = node1;SLTPrint(plist);
}
int main() {//Test1();//Test2();//Test3();//Test4();//Test5();//Test6();//Test7();//Test8();//Test9();//Test10();Test11();return 0;
}

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

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

相关文章

WPF进阶 | WPF 数据绑定进阶:绑定模式、转换器与验证

WPF进阶 | WPF 数据绑定进阶&#xff1a;绑定模式、转换器与验证 一、前言二、WPF 数据绑定基础回顾2.1 数据绑定的基本概念2.2 数据绑定的基本语法 三、绑定模式3.1 单向绑定&#xff08;One - Way Binding&#xff09;3.2 双向绑定&#xff08;Two - Way Binding&#xff09;…

人工智能在医疗领域的应用有哪些?

人工智能在医疗领域的应用十分广泛&#xff0c;涵盖了诊断、治疗、药物研发等多个环节&#xff0c;以下是一些主要的应用&#xff1a; 医疗影像诊断 疾病识别&#xff1a;通过分析 X 光、CT、MRI 等影像&#xff0c;人工智能算法能够识别出肿瘤、结节、骨折等病变&#xff0c;…

【Android】布局文件layout.xml文件使用控件属性android:layout_weight使布局较为美观,以RadioButton为例

目录 说明举例 说明 简单来说&#xff0c;android:layout_weight为当前控件按比例分配剩余空间。且单个控件该属性的具体数值不重要&#xff0c;而是多个控件的属性值之比发挥作用&#xff0c;例如有2个控件&#xff0c;各自的android:layout_weight的值设为0.5和0.5&#xff0…

Ubuntu 24.04 安装 NVIDIA Container Toolkit 全指南:让Docker拥抱GPU

Ubuntu 24.04 安装 NVIDIA Container Toolkit 全指南&#xff1a;让Docker拥抱GPU 前言一、环境准备1.1 验证驱动状态 二、安装NVIDIA Container Toolkit2.1 添加官方仓库2.2 执行安装 三、配置Docker运行时3.1 更新Docker配置 四、验证安装结果4.1 运行测试容器 五、实战应用 …

Qt中Widget及其子类的相对位置移动

Qt中Widget及其子类的相对位置移动 最后更新日期&#xff1a;2025.01.25 下面让我们开始今天的主题… 一、开启篇 提出问题&#xff1a;请看上图&#xff0c;我们想要实现的效果是控件黄色的Widge&#xff08;m_infobarWidget&#xff09;t随着可视化窗口&#xff08;m_glWidge…

3287. 求出数组中最大序列值

3287. 求出数组中最大序列值 题目链接&#xff1a;3287. 求出数组中最大序列值 代码如下&#xff1a; //参考链接&#xff1a;https://leetcode.cn/problems/find-the-maximum-sequence-value-of-array/solutions/3037275/qiu-chu-shu-zu-zhong-zui-da-xu-lie-zhi-b-bhnk cla…

【MySQL】悲观锁和乐观锁的原理和应用场景

悲观锁和乐观锁&#xff0c;并不是 MySQL 或者数据库中独有的概念&#xff0c;而是并发编程的基本概念。 主要区别在于&#xff0c;操作共享数据时&#xff0c;“悲观锁”认为数据出现冲突的可能性更大&#xff0c;而“乐观锁”则是认为大部分情况不会出现冲突&#xff0c;进而…

梯度下降优化算法-RMSProp

RMSProp&#xff08;Root Mean Square Propagation&#xff09;是一种自适应学习率的优化算法&#xff0c;旨在解决 AdaGrad 学习率单调递减的问题。RMSProp 通过引入衰减系数&#xff08;decay rate&#xff09;&#xff0c;使得历史梯度平方和不会无限增长&#xff0c;从而更…

gradle和maven的区别以及怎么选择使用它们

目录 区别 1. 配置方式 2. 依赖管理 3. 构建性能 4. 灵活性和扩展性 5. 多项目构建 如何选择使用 选择 Maven 的场景 选择 Gradle 的场景 区别 1. 配置方式 Maven&#xff1a; 使用基于 XML 的 pom.xml 文件进行配置。所有的项目信息、依赖管理、构建插件等都在这个文…

鲁滨逊漂流记读后感

前言:学校要求出鲁滨逊漂流记的读后感啊&#xff0c;那么今天我就写着试试叭&#xff0c;好久都没更新了嘤&#xff0c;可能写的不好嗷。真的不是很建议参考&#xff0c;因为我的思想可能会与学校的要求不同&#xff0c;更多的是介入了自己的思考&#xff0c;从鲁滨逊好的地方和…

[笔记] 极狐GitLab实例 : 手动备份步骤总结

官方备份文档 : 备份和恢复极狐GitLab 一. 要求 为了能够进行备份和恢复&#xff0c;请确保您系统已安装 Rsync。 如果您安装了极狐GitLab&#xff1a; 如果您使用 Omnibus 软件包&#xff0c;则无需额外操作。如果您使用源代码安装&#xff0c;您需要确定是否安装了 rsync。…

06-机器学习-数据预处理

数据清洗 数据清洗是数据预处理的核心步骤&#xff0c;旨在修正或移除数据集中的错误、不完整、重复或不一致的部分&#xff0c;为后续分析和建模提供可靠基础。以下是数据清洗的详细流程、方法和实战示例&#xff1a; 一、数据清洗的核心任务 问题类型表现示例影响缺失值数值…

【统计的思想】假设检验(二)

假设检验是根据人为设定的显著水平&#xff0c;对被测对象的总体质量特性进行统计推断的方法。 如果我们通过假设检验否定了零假设&#xff0c;只是说明在设定的显著水平下&#xff0c;零假设成立的概率比较小&#xff0c;并不是说零假设就肯定不成立。如果零假设事实上是成立…

2025多目标优化创新路径汇总

多目标优化是当下非常热门且有前景的方向&#xff01;作为AI领域的核心技术之一&#xff0c;其专注于解决多个相互冲突的目标的协同优化问题&#xff0c;核心理念是寻找一组“不完美但均衡”的“帕累托最优解”。在实际中&#xff0c;几乎处处都有它的身影。 但随着需求场景的…

DeepSeek-R1试用

最近DeepSeek太火了&#xff0c;对配置要求不高。刚好放假&#xff0c;下载试试。发现开源大模型的生态做的挺好的&#xff0c;几分钟就能在本地部署一个大模型。 配置 NVIDIA RTX 2060 6GB&#xff08;最低要求 NVIDIA GTX 1060 6GB &#xff09; 下载Ollama Ollama是一个…

Android中Service在新进程中的启动流程2

目录 1、Service在客户端的启动入口 2、Service启动在AMS的处理 3、Service在新进程中的启动 4、Service与AMS的关系再续 上一篇文章中我们了解了Service在新进程中启动的大致流程&#xff0c;同时认识了与客户端进程交互的接口IApplicationThread以及与AMS交互的接口IActi…

C语言--数据在内存中的存储

在C语言中&#xff0c;数据在内存中的存储方式主要取决于数据的类型和存储位置。以下是C语言中数据在内存中的存储方式的详细说明&#xff1a; 1. 数据类型与存储方式 基本数据类型 • 整数类型&#xff08;如int、short、long等&#xff09;&#xff1a; • 存储方式&#x…

【PySide6快速入门】QDialog对话框的使用

文章目录 PySide6快速入门&#xff1a;QDialog对话框的使用前言QDialog的基本用法创建和显示对话框 QDialog的常用函数1. exec()2. accept()3. reject()4. setWindowTitle()5. setModal()6. setFixedSize()7. resize()8. reject()9. setLayout()10. open() 总结 PySide6快速入门…

2748. 美丽下标对的数目(Beautiful Pairs)

2748. 美丽下标对的数目&#xff08;Beautiful Pairs&#xff09; 题目分析 给定一个整数数组 nums&#xff0c;我们需要找出其中符合条件的“美丽下标对”。美丽下标对是指&#xff0c;数组中的某一对数字 nums[i] 和 nums[j]&#xff08;其中 0 ≤ i < j < nums.leng…

计网week1+2

计网 一.概念 1.什么是Internet 节点&#xff1a;主机及其运行的应用程序、路由器、交换机 边&#xff1a;通信链路&#xff0c;接入网链路主机连接到互联网的链路&#xff0c;光纤、网输电缆 协议&#xff1a;对等层的实体之间通信要遵守的标准&#xff0c;规定了语法、语义…