【C语言】实现单链表


目录

(一)头文件

(二)功能实现

(1)打印单链表

(2)头插与头删 

 (3)尾插与尾删

(4) 删除指定位置节点 和 删除指定位置之后的节点

 (5)指定位置之前插入节点 和 指定位置之后插入节点

(6)销毁链表


正文开始:

(一)头文件

         命名为 "LST.h"

        这里不加解释的给出单链表的头文件,并根据头文件来实现单链表的基本功能:

        包括打印单链表,头插与头删,尾插与尾删,删除指定位置的节点,删除指定位置之后的节点,指定位置之前插入节点,指定位置之后插入节点,销毁链表等十个功能。

#pragma once#include<stdio.h>
#include<stdlib.h>
#include<assert.h>//链表的数据类型
typedef int SLTDatatype;//链表的一个节点
typedef struct SLTNode
{SLTDatatype data;struct SLTNode* next;
}SLTNode;//print SLT
void slPrint(SLTNode* phead);//buy_new_new
SLTNode* Get_Newnode(SLTDatatype x);//head_push
void slHeadpush(SLTNode** pphead, SLTDatatype x);//head_del
void slHeaddel(SLTNode** pphead);//tail_push
void slTailpush(SLTNode** pphead,SLTDatatype x);//tail_del
void slTaildel(SLTNode** pphead);//查找数据
SLTNode* sl_find(SLTNode** pphead, SLTDatatype x);/**** FUN 删除指定位置之后的节点* 参数 pos表示data==这个数据的节点* 返回值 无
****/
void SLTEraseAfter(SLTNode* pos);/**** Fun 删除指定位置的节点* 参数 pos* 返回值 无
****/
void slDelPos(SLTNode** pphead, SLTNode* pos);/**** Fun 指定位置之前插入节点* 参数 pos* 返回值 无
****/
void slInsertpos(SLTNode** pphead, SLTNode* pos, SLTDatatype x);//在指定位置之后插入数据
void slInsertAfter(SLTNode* pos, SLTDatatype x);//销毁链表
void sl_destory(SLTNode**pphead);

(二)功能实现

         创建链表(以及初始化):

	SLTNode* pl = NULL;    //链表存储的是空指针,此时表示链表为空

(1)打印单链表

        打印链表并不需要改变链表本身,因此只需要传值(传值意味着形参与实参没有联系)调用;

        通常我们在遍历链表时,总会创建一个pcur指针表示当前指向的节点,这样不会因为遍历而丢失重要指针的值;


//print SLT
void slPrint(SLTNode* phead)
{SLTNode* pcur = phead;while (pcur){printf("%d-->", pcur->data);pcur = pcur->next;}printf("NULL\n");
}

(2)头插与头删 

         在执行插入(包括头插,尾插,特定位置插入)操作时,总会重复使用一个功能:获取新节点,所以我们将获取新节点封装为一个函数:

Get_Newnode():


//buy_new_node
SLTNode* Get_Newnode(SLTDatatype x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc fail");exit(1);}newnode->data = x;newnode->next = NULL;return newnode;
}

头插与头删

头插

        首先传递的指针不为空,通过assert断言来判断;

        申请新节点,并将新节点放在链表的头部;

//head_push
void slHeadpush(SLTNode** pphead, SLTDatatype x)
{//指针不为空assert(pphead);//申请新节点SLTNode* newnode = Get_Newnode(x);//转变头节点newnode->next = *pphead;*pphead = newnode;
}

头删

        首先传递的指针不为空,链表也不为空(如果为空,那么无法执行头删),通过assert断言来判断;

        如果直接将头free掉,就无法对链表的原头解引用(->也是解引用的一种)那么链表的新头就无法找到了。所以创建一个del指针,暂时保存链表的原头(也是将要释放的节点),当链表的头指针后移找到新头后,再通过del释放原头。


//head_del
void slHeaddel(SLTNode** pphead)
{//指针不为空assert(pphead);//链表不为空assert(*pphead);SLTNode* del = *pphead;*pphead = (*pphead)->next;free(del);del = NULL;
}

 (3)尾插与尾删

尾插

         首先传递的指针不为空,通过assert断言来判断;

        尾插通常是在尾部插入,但是如果链表为空,尾插就变成了头插;

        如果链表为空,新节点作为头节点;链表不为空,找到尾节点,进行尾插;

//tail_push
void slTailpush(SLTNode** pphead, SLTDatatype x)
{assert(pphead);SLTNode* newnode = Get_Newnode(x);//链表为空,新节点作为头节点if (*pphead == NULL){*pphead = newnode;return;}SLTNode* pcur = *pphead;//链表不为空,找到尾节点while (pcur->next){pcur = pcur->next;}pcur->next = newnode;
}

尾删

        首先传递的指针不为空,链表也不为空(如果为空,那么无法执行尾删),通过assert断言来判断;

        尾删通常是删除尾部的节点,如果只有一个节点,尾删就变成了头删;

        如果只有一个节点,直接删除;如果有多个节点,现找尾,再执行尾删;


//tail_del
void slTaildel(SLTNode** pphead)
{assert(pphead);assert(*pphead);//链表不为空//只有一个节点if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;return;}//有多个节点SLTNode* prev = NULL;SLTNode* ptail = *pphead;//找尾while (ptail->next){prev = ptail;ptail = ptail->next;}prev->next = NULL;//销毁尾节点free(ptail);ptail = NULL;
}

(4) 删除指定位置节点 和 删除指定位置之后的节点

        指定位置指定的是某一个存在于链表中的数据的位置,这意味着我们先要找到这个已经存在的数据,封装一个函数:

        sl_find():

//查找数据
SLTNode* sl_find(SLTNode** pphead, SLTDatatype x)
{SLTNode* pcur = *pphead;while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}

删除指定位置之后的数据

        相比于删除指定位置,删除指定位置之后更简单;

        因为删除指定位置之后仅需要指定位置的节点,不需要遍历查找;删除指定位置需要知道指定位置之前的节点,需要遍历查找;

删除指定位置之后

        首先传递的指针不为空,这个位置也不能是尾节点(尾节点后面没有可以删除的节点),通过assert断言来判断;

        如果直接将节点free掉(设指定位置为P节点),就无法对P节点解引用(->也是解引用的一种)那么链表P节点后的就无法找到了。所以创建一个del指针,暂时保存链表的P节点后的指针,当P前与P后相连后,再通过del释放P节点。


/**** FUN 删除指定位置之后的节点* 参数 pos表示data==这个数据的节点* 返回值 无
****/
void SLTEraseAfter(SLTNode* pos)
{assert(pos);//pos->next不能为空assert(pos->next);//pos  pos->next  pos->next->nextSLTNode* del = pos->next;pos->next = pos->next->next;free(del);del = NULL;
}

删除指定位置

        首先传递的指针不为空,链表也不为空(如果为空,那么无法执行删除),通过assert断言来判断;

        如果pos 刚好是头节点,直接删除;

        pos不是头节点,找到pos,再执行删除;先创建一个prev节点,遍历链表,指向P节点之前;


/**** Fun 删除指定位置的节点* 参数 pos* 返回值 无
****/
void slDelPos(SLTNode** pphead, SLTNode* pos)
{assert(pphead);assert(*pphead);assert(pos);//pos 刚好是头节点,直接删除if (*pphead == pos){slHeaddel(pphead);return;}//pos不是头节点,找到posSLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//链接prev->next = pos->next;free(pos);pos = NULL;
}

 (5)指定位置之前插入节点 和 指定位置之后插入节点

         指定位置之前插入节点 与 指定位置之前删除节点类似,都需要创建prev指针,遍历链表;

指定位置之前插入节点 

        首先传递的指针不为空,链表也不为空(如果pos不为空,所以链表一定不为空,【因为链表为空是pos为空的充分条件】两者是逆否命题),通过assert断言来判断;

         如果pos是头节点,则头插;pos不是头节点,则找到pos,通过prev插入新节点;


/**** Fun 指定位置之前插入节点* 参数 pos* 返回值 无
****/
void slInsertpos(SLTNode** pphead, SLTNode* pos,SLTDatatype x)
{assert(pphead);assert(pos);//要加上链表不能为空assert(*pphead);SLTNode* newnode = Get_Newnode(x);//pos是头节点if (*pphead == pos){slHeadpush(pphead, x);return;}//pos不是头节点SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}newnode->next = pos;prev->next = newnode;
}

指定位置之后插入节点

        直接插入即可;


//在指定位置之后插入数据
void slInsertAfter(SLTNode* pos, SLTDatatype x)
{assert(pos);SLTNode* newnode = Get_Newnode(x);newnode->next = pos->next;pos->next = newnode;
}

(6)销毁链表

       首先传递的指针不为空,通过assert断言来判断;

        通过循环一个接着一个释放,在每次释放之前,创建一个next指针保存下一个节点,防释放后无法通过解引用找到下一个节点;

        最后,将链表置空;


//销毁链表
void sl_destory(SLTNode**pphead)
{assert(pphead);SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

完~

未经作者同意禁止转载 

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

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

相关文章

k8s搭建容器云平台

平台部署–部署容器云平台 部署kubernets 1.22.1集群 curl -O http://...../chinaskills_cloud_paas_v2.0.2.iso mount chinaskills_cloud_paas_v2.0.2.iso /mnt cp -rvf /mnt/* /opt cp /opt/kubeeasy /usr/bin kubeeasy --help#[install dependencies package cluster]kube…

什么是依赖注入?如何在Spring中配置和使用依赖注入?

什么是依赖注入&#xff1f; 依赖注入&#xff08;Dependency Injection&#xff0c;DI&#xff09;是一种设计模式&#xff0c;它通过将对象之间的依赖关系的创建和维护转移到外部容器中来&#xff0c;以减少对象之间的紧耦合性并提高可重用性。在传统的程序设计中&#xff0c…

蓝桥杯嵌入式第9届真题(完成) STM32G431

蓝桥杯嵌入式第9届真题(完成) STM32G431 题目 分析和代码 main.h /* USER CODE BEGIN Header */ /********************************************************************************* file : main.h* brief : Header for main.c file.* …

[ai笔记4] 将AI工具场景化,应用于生活和工作

欢迎来到文思源想的AI空间&#xff0c;这是技术老兵重学ai以及成长思考的第4篇分享内容&#xff01; 转眼已经到了大年初三&#xff0c;但是拜年的任务还只完成了一半&#xff0c;准备的大部头的书&#xff0c;现在也就看了两本&#xff0c;还好AI笔记通过每天早起坚持了下来。…

Java-并发高频面试题-2

接着之前的Java-并发高频面试题 7. synchronized的实现原理是怎么样的&#xff1f; 首先我们要知道synchronized它是解决线程安全问题的一种方式&#xff0c;而具体是怎么解决的呢&#xff1f;主要是通过加锁的方式来解决 在底层实现上来看 是通过 monitorenter、monitorexit…

java实战:基于Redis的ZSet实现秒级排行榜

本文将介绍如何使用Redis的ZSet&#xff08;有序集合&#xff09;来实现一个秒级排行榜。展示如何使用Java和Jedis库来创建、更新和获取排行榜数据。通过本文&#xff0c;可以了解到如何在Java应用程序中使用Redis的ZSet来实现一个高性能的秒级排行榜。 一、ZSet概述 Redis的…

【Spring原理进阶】SpringMVC调用链+JSP模板应用讲解

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《Spring 狂野之旅&#xff1a;底层原理高级进阶》 &#x1f680…

【Python网络编程之Ping命令的实现】

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;Python开发技术 &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; Python网络编程之Ping命令的实现 代码见资源&#xff0c;效果图如下一、实验要求二、协议原理2…

所谓的意志力,也许根本就不存在

许多讲自我提升的书&#xff0c;往往会把成功的原因归结为两点&#xff1a;自律&#xff0c;以及专注。 他们会告诉你&#xff1a;为什么别人能够成功、而你不能&#xff1f;第一是你不够自律&#xff0c;无法每天雷打不动地坚持「好习惯」&#xff1b;第二&#xff0c;是你不够…

[Java][算法 滑动窗口]Day 02---LeetCode 热题 100---08~09

第一题 无重复字符串的最长子串 思路 其实就是在字符串S中 找到没有重复的最长子串的长度 这道题的难点就是在于如何判断最长并且无重复 首先 最长长度 可以使用变量max记录保存 再者 判断有无重复 最简单的方法就是 暴力遍历法 即对于每次找的子串都再次寻找遍历…

操作字符串之子串替换-13-${string//substring/replacement}

1.${string//substring/replacement} 使用$replacement来替换所有匹配的$substring 2.实例 操作字符串样例&#xff1a;stringabc123ABC456xyzabc 字符串操作默认从左边开始进行 命令&#xff1a; echo ${string//abc/ZTJ} [rootkibana ~]# echo ${string//abc/ZTJ} ZTJ…

Validation-参数校验框架

在代码完成后期,为保证数据的正确性和完整性,需要在后端对接口请求的参数进行校验。使用Spring的Validation框架&#xff0c;通过添加注解就可以完成参数校验&#xff0c;不用写if/else来逻辑判断了。 使用该框架&#xff0c;首先在请求参数接收层也就是controller层上方标注V…

【Chrono Engine学习总结】5-sensor-5.1-sensor基础并创建一个lidar

由于Chrono的官方教程在一些细节方面解释的并不清楚&#xff0c;自己做了一些尝试&#xff0c;做学习总结。 1、Sensor模块 Sensor模块是附加模块&#xff0c;需要单独安装。参考&#xff1a;【Chrono Engine学习总结】1-安装配置与程序运行 Sensor Module Tutorial Sensor …

【国产MCU】-CH32V307-基本定时器(BCTM)

基本定时器(BCTM) 文章目录 基本定时器(BCTM)1、基本定时器(BCTM)介绍2、基本定时器驱动API介绍3、基本定时器使用实例CH32V307的基本定时器模块包含一个16 位可自动重装的定时器(TIM6和TIM7),用于计数和在更新新事件产生中断或DMA 请求。 本文将详细介绍如何使用CH32…

苹果Mac键盘如何将 F1 到 F12 取消按Fn

苹果电脑安装了Win10操作系统之后&#xff0c;F1到F12用不了怎么办的解决方法。本文将介绍一些解决方法&#xff0c;帮助您解决无法使用F1到F12功能键的问题。 使用 Mac系统的人都知道&#xff0c;Mac系统默认是没有开启 F1-F12 的使用的&#xff0c;平时我们使用的系统都可以使…

AcWing 802. 区间和 离散化

文章目录 题目链接题目描述解题思路代码实现总结 题目链接 链接: AcWing 802. 区间和 题目描述 解题思路 离散化是一种常用的技巧&#xff0c;它能够将原始的连续数值转换为一组离散的值&#xff0c;从而简化问题的处理。在这段代码中&#xff0c;离散化的过程主要分为三个步…

2024全栈元年-thinkphp-数据操作

thinkphp 数据相关操作 1.单数据查询 1、单数据查询 ,Db::table(‘tp_stu’) 必须加前缀 2、如果只是查询符合条件的使用where find,如果没有符合条件的返回null 3、使用 findOrFail 没有数据会抛出异常 4、使用 findOrEmpty 没有数据会返回【】 5、得到最近一个原生SQL …

C++重新入门-指针篇

C 中的指针是一种非常重要的数据类型&#xff0c;它们存储了内存地址&#xff0c;可以用来直接访问和操作内存中的数据。指针提供了灵活性和直接性&#xff0c;但也需要谨慎使用&#xff0c;因为误用指针可能导致程序崩溃或安全漏洞。以下是对 C 指针的详细解释&#xff1a; 1…

那些也许你不知道的操作符!

前言 操作符有很多种&#xff0c;目前我们已经了解了一部分 例如最简单的、-、*、/、&#xff0c;还有我们学到的&&&#xff0c;||&#xff0c;!等&#xff0c;但是操作符可不是就只有这么些的&#xff0c;让我们一起来看看吧 目录 1. 移位操作符 原码、反码、补码…

当go get获取不到软件包时

当使用go get命令获取软件包时&#xff0c;如果无法成功获取&#xff0c;您可以尝试以下方法来解决问题&#xff1a; 检查网络连接&#xff1a;首先&#xff0c;确保您的计算机能够访问互联网&#xff0c;并且没有任何网络防火墙或代理设置阻止了go get命令的正常运行。 设置代…