数据结构-->线性表-->单链表

 

链表的定义

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

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

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

链表的节点(结点)

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

给出每个节点对应的结构体代码:以保存的节点是整形为例:

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

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

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

节点一般是从堆上申请的

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

链表的分类 

对于链表的分类来说:一共有8种

最常用的两种链表结构 

虽然链表结构很多,但最常用的只有两种结构
单链表:也叫无头单向非循环链表,结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构。
带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。这个链表虽然结构复杂,但是使用代码实现以后会发现法结构会带来更多优势。

 

首先我们给出我们要实现的单链表的头文件:

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLDataType;
typedef struct SList
{SLDataType data;struct SList* next;
}SL;
//打印
void SLPrint(SL* phead);
//销毁
void SLDestroy(SL** pphead);
//开辟新链表节点
SL* Buynode(SLDataType x);
//链表头插
void SLPushFront(SL** pphead,SLDataType x);
//链表尾插
void SLPushBack(SL** pphead,SLDataType x);
//链表头删
void SLPopFront(SL** pphead); 
//链表尾删
void SLPopBack(SL** pphead);
//链表对节点的查找
SL* SLFind(SL** pphead,SLDataType x);
//对指定位置之前插入数据
void SLInsert(SL** pphead,SL* pos,SLDataType x);
//在指定位置之后插入数据
void SLInsertafter(SL* pos, SLDataType x);
//删除pos节点
void SLErase(SL** pphead,SL* pos);
//删除pos之后的节点
void SLEraseafter(SL* pos);

对单链表打印


这里直接进行遍历就ok

//打印
void SLPrint(SL* phead)
{SL* pcur = phead;while (pcur){printf("%d->", pcur->data);pcur = pcur->next;}printf("NULL");
}

销毁单链表 

逐个销毁,销毁某一个节点时,保存他的下一个节点的地址。

//销毁
void SLDestroy(SL** pphead)
{assert(pphead);assert(*pphead);SL* pcur = *pphead;while (pcur){SL* next = (*pphead)->next;free(pcur);pcur = next;}*pphead = NULL;
}

 开辟节点空间

为新的数据开辟空间

//开辟节点空间
SL* Buynode(SLDataType x)
{SL* newnode = (SL*)malloc(sizeof(SL));if (newnode == NULL){perror("malloc fail\n");return;}newnode->data = x;newnode->next = NULL;return newnode;
}

 单链表头插

记得先后顺序,个人感觉容易犯错

//链表头插
void SLPushFront(SL** pphead, SLDataType x)
{assert(pphead);SL* new = Buynode(x);//个人觉得易错new->next = *pphead;*pphead = new;
}

单链表尾插

如果头结点为空,则相当于头插

如果头结点不为空,就正常找尾节点然后插入。

//链表尾插
void SLPushBack(SL** pphead, SLDataType x)
{assert(pphead);SL* new = Buynode(x);if (*pphead == NULL){*pphead = new;return;}SL* pcur = *pphead;while (pcur->next){pcur = pcur->next;}pcur->next = new;
}

 单链表头删

 对于删除来说,需要断言pphead和*pphead,pphead是存放*pphead的地址不能为NULL,

*pphead是为了判断单链表不能为NULL

//链表头删
void SLPopFront(SL** pphead)
{assert(pphead);assert(*pphead);SL* next = (*pphead)->next;free(*pphead);*pphead = next;
}

 单链表尾删

如果只有一个元素,就相当于头删,否则就相当于尾删

//链表尾删
void SLPopBack(SL** pphead)
{assert(pphead);assert(*pphead);if (((*pphead)->next) == NULL){free(*pphead);*pphead = NULL;}SL* prev = NULL;SL* pcur = *pphead;while (pcur->next){prev = pcur;pcur = pcur->next;}free(pcur);pcur = NULL;prev->next = NULL;
}

单链表对节点的查找

遍历查找与目标值相同的,然后返回存储该值的地址

//链表对节点的查找
SL* SLFind(SL** pphead, SLDataType x)
{assert(pphead);SL* pcur = *pphead;while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}

对指定位置插入数据

1.pos在*pphead,相当于头插

2.pos不在*pphead,就正常插入

//对指定位置之前插入数据
void SLInsert(SL** pphead, SL* pos, SLDataType x)
{assert(pphead);assert(pos);assert(*pphead);SL* new = Buynode(x);if (pos == *pphead){SLPushFront(pphead, x);return;}SL* pcur = *pphead;while (pcur->next!=pos){pcur = pcur->next;}pcur->next = new;new->next = pos;
}

 在指定位置后插入

没什么好说的,直接找到指定位置,然后插入即可

//在指定位置之后插入数据
void SLInsertafter(SL* pos, SLDataType x)
{assert(pos);SL* new = Buynode(x);new->next = pos->next;pos->next = new;
}

 删除pos节点的值

如果pos为*pphead就头删,否则就正常删除pos节点的值

//删除pos节点
void SLErase(SL** pphead, SL* pos)
{assert(pphead);assert(*pphead);assert(pos);if (*pphead == pos){SLPopFront(pphead);return;}SL* pcur = *pphead;while (pcur->next != pos){pcur = pcur->next;}SL* next = pos->next;pcur->next = next;free(pos);pos=NULL;
}

 删除pos节点之后的值

找到pos节点,当然联系pos和pos->next->next的值

//删除pos之后的节点
void SLEraseafter(SL* pos)
{assert(pos);assert(pos->next);SL* pcur = pos->next;pos->next = pos->next->next;free(pcur);pcur = NULL;
}

 最终代码

SList.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLDataType;
typedef struct SList
{SLDataType data;struct SList* next;
}SL;
//打印
void SLPrint(SL* phead);
//销毁
void SLDestroy(SL** pphead);
//开辟新链表节点
SL* Buynode(SLDataType x);
//链表头插
void SLPushFront(SL** pphead,SLDataType x);
//链表尾插
void SLPushBack(SL** pphead,SLDataType x);
//链表头删
void SLPopFront(SL** pphead); 
//链表尾删
void SLPopBack(SL** pphead);
//链表对节点的查找
SL* SLFind(SL** pphead,SLDataType x);
//对指定位置之前插入数据
void SLInsert(SL** pphead,SL* pos,SLDataType x);
//在指定位置之后插入数据
void SLInsertafter(SL* pos, SLDataType x);
//删除pos节点
void SLErase(SL** pphead,SL* pos);
//删除pos之后的节点
void SLEraseafter(SL* pos);

SList.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "SList.h"
//打印
void SLPrint(SL* phead)
{SL* pcur = phead;while (pcur){printf("%d->", pcur->data);pcur = pcur->next;}printf("NULL");
}
//销毁
void SLDestroy(SL** pphead)
{assert(pphead);assert(*pphead);SL* pcur = *pphead;while (pcur){SL* next = (*pphead)->next;free(pcur);pcur = next;}*pphead = NULL;
}
//开辟节点空间
SL* Buynode(SLDataType x)
{SL* newnode = (SL*)malloc(sizeof(SL));if (newnode == NULL){perror("malloc fail\n");return;}newnode->data = x;newnode->next = NULL;return newnode;
}
//链表头插
void SLPushFront(SL** pphead, SLDataType x)
{assert(pphead);SL* new = Buynode(x);//个人觉得易错new->next = *pphead;*pphead = new;
}
//链表尾插
void SLPushBack(SL** pphead, SLDataType x)
{assert(pphead);SL* new = Buynode(x);if (*pphead == NULL){*pphead = new;return;}SL* pcur = *pphead;while (pcur->next){pcur = pcur->next;}pcur->next = new;
}
//链表头删
void SLPopFront(SL** pphead)
{assert(pphead);assert(*pphead);SL* next = (*pphead)->next;free(*pphead);*pphead = next;
}
//链表尾删
void SLPopBack(SL** pphead)
{assert(pphead);assert(*pphead);if (((*pphead)->next) == NULL){free(*pphead);*pphead = NULL;}SL* prev = NULL;SL* pcur = *pphead;while (pcur->next){prev = pcur;pcur = pcur->next;}free(pcur);pcur = NULL;prev->next = NULL;
}
//链表对节点的查找
SL* SLFind(SL** pphead, SLDataType x)
{assert(pphead);SL* pcur = *pphead;while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}
//对指定位置之前插入数据
void SLInsert(SL** pphead, SL* pos, SLDataType x)
{assert(pphead);assert(pos);assert(*pphead);SL* new = Buynode(x);if (pos == *pphead){SLPushFront(pphead, x);return;}SL* pcur = *pphead;while (pcur->next!=pos){pcur = pcur->next;}pcur->next = new;new->next = pos;
}
//在指定位置之后插入数据
void SLInsertafter(SL* pos, SLDataType x)
{assert(pos);SL* new = Buynode(x);new->next = pos->next;pos->next = new;
}
//删除pos节点
void SLErase(SL** pphead, SL* pos)
{assert(pphead);assert(*pphead);assert(pos);if (*pphead == pos){SLPopFront(pphead);return;}SL* pcur = *pphead;while (pcur->next != pos){pcur = pcur->next;}SL* next = pos->next;pcur->next = next;free(pos);pos=NULL;
}
//删除pos之后的节点
void SLEraseafter(SL* pos)
{assert(pos);assert(pos->next);SL* pcur = pos->next;pos->next = pos->next->next;free(pcur);pcur = NULL;
}

Test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "SList.h"
int main()
{SL* plist = NULL;SLPushBack(&plist, 1);SLPushBack(&plist, 2);//1->2//SLPrint(plist);SLPushFront(&plist, 3);//3->1->2//SLPrint(plist);SLPushFront(&plist, 4);//SLPrint(plist);//4->3->1->2SLPopBack(&plist);//4->3->1//SLPrint(plist);SLPopFront(&plist);//3->1//SLPrint(plist);SL* new=SLFind(&plist, 3);SLInsert(&plist,new,4);//4->3->1//SLPrint(plist);SLInsertafter(new, 5);//4->3->5->1//SLPrint(plist);SLErase(&plist, new);//4->5->1SLPrint(plist);SLDestroy(&plist);return 0;
}

运行test函数:

以及测试过了,每个接口函数都没啥问题

 

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

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

相关文章

C语言学习day12:水仙花(do while)

前面我们学习了do while循环&#xff0c;今天做一个练习&#xff1a;水仙花 题目&#xff1a;一个三位数&#xff08;100-999&#xff09;&#xff0c;获取其中所有各个位数的相加等于这个数本身 的数 尽量自己先写一写&#xff0c;差不多了再看答案 思路&#xff1a; 先获…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Toggle组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Toggle组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Toggle组件 组件提供勾选框样式、状态按钮样式及开关样式。 子组件 仅当Toggl…

【机器学习】合成少数过采样技术 (SMOTE)处理不平衡数据(附代码)

1、简介 不平衡数据集是机器学习和人工智能中普遍存在的挑战。当一个类别中的样本数量明显超过另一类别时&#xff0c;机器学习模型往往会偏向大多数类别&#xff0c;从而导致性能不佳。 合成少数过采样技术 (SMOTE) 已成为解决数据不平衡问题的强大且广泛采用的解决方案。 …

「深度学习」dropout 技术

一、工作原理 1. 正则化网络 dropout 将遍历网络的每一层&#xff0c;并设置消除神经网络中节点的概率。 1. 每个节点保留/消除的概率为0.5: 2. 消除节点&#xff1a; 3. 得到一个规模更小的神经网络&#xff1a; 2. dropout 技术 最常用&#xff1a;反向随机失活 "…

SPSS双变量相关分析

双变量相关分析通过计算皮尔逊简单相关系数、斯皮尔曼等级相关系数、肯德尔等级相关系数及其显著性水平展开。其中皮尔逊简单相关系数是一种线性关联度量&#xff0c;适用于变量为定量连续变量且服从正态分布、相关关系为线性时的情形。如果变量不是正态分布的&#xff0c;或具…

Git远程仓库的使用(Gitee)及相关指令

目录 1 远程仓库的创建和配置 1.1 创建远程仓库 1.2 设置SSH公钥 2 指令 2.1 git remote add 远端名称(一般为origin) 仓库路径 2.2 git remote 2.3 git push [-f] [--set-upstream] [远端名称 [本地分支名][:远端分支名]] 2.3 git clone url 2.4 git fetch 2.5 git p…

Python实战:用Python程序实现春晚刘谦魔术

刘谦春晚魔术是一个让人叹为观止的魔术表演&#xff0c;其中涉及到了数学、编程和创意的结合。看了春晚魔术的朋友们&#xff0c;是不是好奇春晚刘谦的魔术是怎么变的。 在这篇文章中&#xff0c;我们将通过 Python 程序实现春晚刘谦魔术&#xff0c;让读者对这个魔术有更深入…

2024年N1叉车司机证模拟考试题库及N1叉车司机理论考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年N1叉车司机证模拟考试题库及N1叉车司机理论考试试题是由安全生产模拟考试一点通提供&#xff0c;N1叉车司机证模拟考试题库是根据N1叉车司机最新版教材&#xff0c;N1叉车司机大纲整理而成&#xff08;含2024年…

MIPS指令集处理器设计(支持64条汇编指令)

一、题目背景和意义 二、国内外研究现状 (略) 三、MIPS指令集处理器设计与实现 (一).MIPS指令集功能性梳理 1.MIPS指令集架构 (1).mips基础指令集格式总结 MIPS是&#xff08;Microcomputer without interlocked pipeline stages&#xff09;[10]的缩写&#xff0c;含义是…

《动手学深度学习(PyTorch版)》笔记7.7

注&#xff1a;书中对代码的讲解并不详细&#xff0c;本文对很多细节做了详细注释。另外&#xff0c;书上的源代码是在Jupyter Notebook上运行的&#xff0c;较为分散&#xff0c;本文将代码集中起来&#xff0c;并加以完善&#xff0c;全部用vscode在python 3.9.18下测试通过&…

Flutter开发iOS问题记录

一、版本适配问题 warning: The iOS deployment target ‘IPHONEOS_DEPLOYMENT_TARGET’ is set to 10.0, but the range of supported deployment target versions is 12.0 to 17.2.99. (in target ‘Protobuf’ from project ‘Pods’) 可以通过在podfile中配置解决。 pos…

数字孪生:构建未来智慧社区的关键技术

随着科技的快速发展&#xff0c;数字孪生技术作为构建未来智慧社区的关键技术&#xff0c;正逐渐受到广泛关注。数字孪生技术能够实现物理世界与数字世界的交互映射&#xff0c;为智慧社区的建设提供强有力的支持。本文将探讨数字孪生技术在构建未来智慧社区中的作用和意义&…