单向无头链表实现

目录

1. 为什么要有链表?

2. 链表的种类

 3. 具体功能实现

(1)节点结构体定义

(2)申请节点

(3)尾插

(4)尾删

(5)头插

(6)头删

(7)查找

(8)指定位置插入

(9)插入指定位置后面

(10)消除指定位置元素

(11)消除指定位置后的数据

(12)打印链表

(13)销毁链表

4. 完整代码

1.  slist.c

2.  slist.h

 3. 测试文件


1. 为什么要有链表?

上篇文章我们介绍了顺序表,顺序表有很多缺陷比如

1. 空间不够需要增容,增容需要付出代价

2. 为了避免频繁扩容,我们满了基本上都是扩2倍,可能会导致一定的空间浪费

3. 要求数据从开始位置连续存储,我们在头部或中间插入删除数据,需要挪动数据,效率不高

 那有人就针对顺序表的诸多缺陷,设计出了链表

链表的优缺点如下:

优点:
按需申请空间,不用了就释放空间,头部中间插入删除数据不需要挪动数据,不存在空间浪费。

缺点:

每存一个数据都要存一个指针去链接后面的内存节点,不支持随机访问(用下标直接访问第i个)

2. 链表的种类

单向或双向,带头或不带头,循环或不循环

共八种

最常用的为

1. 无头单向非循环链表

结构简单,更多作为其他数据结构的子结构

2. 带头双向循环链表 

结构复杂,一般用于单独存储数据,实际使用中结构复杂实现简单

 3. 具体功能实现

(1)节点结构体定义
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
typedef int SLTDataType;typedef struct SListNode
{int data;struct SListNode* next;}SListNode;

data 用来存储数据

next用来存储下一个节点的地址

(2)申请节点

插入数据时节省代码量

SListNode* BuySListNode(SLTDataType x)
{SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));if (newnode == NULL){exit(-1);}newnode->data = x;newnode->next = NULL;return newnode;
}
(3)尾插

在链表的末尾链接上一个节点

void SListPushBack(SListNode** phead, SLTDataType x)
{	 SListNode* newnode = BuySListNode(x);if (*phead == NULL){*phead = newnode;}else{SListNode* tail = *phead;while (tail->next != NULL){tail = tail->next;}tail->next = newnode;}}
(4)尾删

删除链表最末尾的那个节点

void SListPopBack(SListNode** phead)
{	if (*phead == NULL)return;//assert(*phead!=NULL);if ((*phead)->next == NULL){free(*phead);*phead = NULL;}else{SListNode* tail = *phead;SListNode* t = tail;while (tail->next != NULL){t = tail;tail = tail->next;}free(tail);tail = NULL;t->next = NULL;//SListNode* tail = *phead;//while (tail->next->next != NULL)//{//	tail = tail->next;//}//free(tail->next);//tail->next = NULL;}}
(5)头插

在链表头部插入数据

void SListPushFront(SListNode** phead, SLTDataType x)
{SListNode* newnode = BuySListNode(x);newnode->next = *phead;*phead = newnode;
}
(6)头删

删除链表头部的数据

void SListPopFront(SListNode** phead)
{if (*phead == NULL)return;SListNode* front=(*phead)->next;free(*phead);*phead = front;
}
(7)查找

查找链表中的某个数据,返回地址,找不到返回空指针

SListNode* SListFind(SListNode* phead, SLTDataType x)
{SListNode* cur = phead;while (cur){if (cur->data == x){return cur;}else{cur = cur->next;}}return  NULL;
}
(8)指定位置插入

根据给的地址插入数据(插入到指定位置前面)

void SListInsert(SListNode** phead, SListNode* pos, SLTDataType x)//pos配合查找函数
{SListNode* newnode = BuySListNode(x);if (*phead == pos){void SListPopFront(phead);}//找到pos的前一位置else{ SListNode* posPrey = *phead;while (posPrey->next != pos){posPrey = posPrey->next;}posPrey->next = newnode;newnode->next = pos;}
}
(9)插入指定位置后面
void SListInsertTail(SListNode* pos, SLTDataType x)
{assert(pos);SListNode* newnode = BuySListNode(x);newnode->next = pos->next;pos->next = newnode;
}
(10)消除指定位置元素
void SListErase(SListNode** phead, SListNode* pos)
{assert(head && (*head));assert(pos);if (*phead == pos){*phead = (*phead)->next;free(pos);pos = NULL;}else{SListNode* prev = *phead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
}
(11)消除指定位置后的数据
void SListEraseAfter(SListNode* pos)
{assert(pos);if (pos == NULL){exit(-1);}SListNode* next = pos->next;pos->next = next->next;free(next);next = NULL;
}
(12)打印链表
void SListPrint(SListNode* phead)
{SListNode* cur = phead;while (cur != NULL){printf("%d->", cur->data);cur = cur->next;}printf("\n");
}

(13)销毁链表

使用结束后要销毁

void SListDestroy(SListNode** phead)
{assert(phead && *phead);SListNode* plist = *phead;while (plist->next != NULL){plist = plist->next;free(*phead);*phead = plist;}free(*phead);*phead = NULL;}

4. 完整代码

1.  slist.c
#define _CRT_SECURE_NO_WARNINGS h
#include"sllist.h"SListNode* BuySListNode(SLTDataType x)
{SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));if (newnode == NULL){exit(-1);}newnode->data = x;newnode->next = NULL;return newnode;
}void SListPrint(SListNode* phead)
{SListNode* cur = phead;while (cur != NULL){printf("%d->", cur->data);cur = cur->next;}printf("\n");
}void SListPushBack(SListNode** phead, SLTDataType x)
{	 SListNode* newnode = BuySListNode(x);if (*phead == NULL){*phead = newnode;}else{SListNode* tail = *phead;while (tail->next != NULL){tail = tail->next;}tail->next = newnode;}}void SListPushFront(SListNode** phead, SLTDataType x)
{SListNode* newnode = BuySListNode(x);newnode->next = *phead;*phead = newnode;
}void SListPopBack(SListNode** phead)
{	if (*phead == NULL)return;//assert(*phead!=NULL);if ((*phead)->next == NULL){free(*phead);*phead = NULL;}else{SListNode* tail = *phead;SListNode* t = tail;while (tail->next != NULL){t = tail;tail = tail->next;}free(tail);tail = NULL;t->next = NULL;//SListNode* tail = *phead;//while (tail->next->next != NULL)//{//	tail = tail->next;//}//free(tail->next);//tail->next = NULL;}}void SListPopFront(SListNode** phead)
{if (*phead == NULL)return;SListNode* front=(*phead)->next;free(*phead);*phead = front;
}SListNode* SListFind(SListNode* phead, SLTDataType x)
{SListNode* cur = phead;while (cur){if (cur->data == x){return cur;}else{cur = cur->next;}}return  NULL;
}
void SListInsert(SListNode** phead, SListNode* pos, SLTDataType x)//pos配合查找函数
{SListNode* newnode = BuySListNode(x);if (*phead == pos){void SListPopFront(phead);}//找到pos的前一位置else{ SListNode* posPrey = *phead;while (posPrey->next != pos){posPrey = posPrey->next;}posPrey->next = newnode;newnode->next = pos;}
}
void SListInsertTail(SListNode* pos, SLTDataType x)
{assert(pos);SListNode* newnode = BuySListNode(x);newnode->next = pos->next;pos->next = newnode;
}
void SListErase(SListNode** phead, SListNode* pos)
{assert(head && (*head));assert(pos);if (*phead == pos){*phead = (*phead)->next;free(pos);pos = NULL;}else{SListNode* prev = *phead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
}
void SListEraseAfter(SListNode* pos)
{assert(pos);if (pos == NULL){exit(-1);}SListNode* next = pos->next;pos->next = next->next;free(next);next = NULL;
}
void SListDestroy(SListNode** phead)
{assert(phead && *phead);SListNode* plist = *phead;while (plist->next != NULL){plist = plist->next;free(*phead);*phead = plist;}free(*phead);*phead = NULL;}
2.  slist.h
#define _CRT_SECURE_NO_WARNINGS h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
typedef int SLTDataType;typedef struct SListNode
{int data;struct SListNode* next;}SListNode;void SListPrint(SListNode* phead);
//打印
void SListPushBack(SListNode** phead, SLTDataType x);
//尾插
void SListPushFront(SListNode** phead,SLTDataType x);
//头插
void SListPopBack(SListNode** phead);
//尾删
void SListPopFront(SListNode** phead);
//头删
SListNode* SListFind(SListNode* phead, SLTDataType x);
//找位置
//在pos位置之前去插入一个节点
void SListInsert(SListNode** phead, SListNode* pos, SLTDataType x);
//在pos前面插入//void SListInsert(SListNode* phead, int pos, SLTDataType x);void SListErase(SListNode** phead, SListNode* pos);
//找坐标删除
void SListEraseAfter(SListNode* pos);
//删除坐标后面的值void SListDestroy(SListNode** phead)
 3. 测试文件
#define _CRT_SECURE_NO_WARNINGS h
#include"sllist.h"
#include<stdio.h>
#include<stdlib.h>
void TestSList1()
{SListNode* plist = NULL;//初始值SListPushBack(&plist, 1);SListPushBack(&plist, 2);SListPushBack(&plist, 3);SListPushFront(&plist, 4);SListPushFront(&plist, 5);SListPrint(plist);SListPopFront(&plist);SListPopFront(&plist);SListPrint(plist);
}
void TestSList2()
{SListNode* plist = NULL;//初始值SListPushBack(&plist, 1);SListPushBack(&plist, 2);SListPushBack(&plist, 3);SListPushBack(&plist, 3);SListPushBack(&plist, 2);SListNode* pos = SListFind(plist, 2);SListInsert(&plist ,pos,20);SListPrint(plist);//SListDestroy(&plist);//SListPrint(plist);int i = 1;while (pos){printf("第%d个节点%p->%d\n", i++, pos, pos->data);pos = SListFind(pos->next, 2);}pos = SListFind(plist, 3);while(pos){pos->data = 30;pos = SListFind(pos->next, 3);}SListPrint(plist);
}
typedef struct xx
{int* n;
}xx;
int main()
{TestSList2();//xx p;//int a = 0;//p.n = &a;//a = 2;//printf("%d", *(p.n));return 0;
}

这篇文章就到这里了,希望可以帮到您

(๑′ᴗ‵๑)I Lᵒᵛᵉᵧₒᵤ❤

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

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

相关文章

面向对象-----继承

前面向大家介绍了面向对象中的封装性&#xff0c;今天再来向大家介绍面向对象的继承和多态的两大特性。 1.继承 1.1 为什么需要继承&#xff1f; 在java语言中&#xff0c;我们用类来描述世间万物&#xff0c;虽然万物非常复杂&#xff0c;但总有一些共同点&#xff0c;如果…

CAD2023 2024 2025以上版本出现无法运行 AutoCAD,原因可能如下1) 此版本的 AutoCAD 安装不正确

错误提示如下 此版本的 AutoCAD 安装不正确 缺少依赖组件Microsoft Edge webview2 Runtime 缺少依赖组件 Microsoft.NET跟You must install .NET Desktop Runtime 打开autoremove&#xff0c;点击扩展&#xff0c;输入 无法运行&#xff0c;点击搜索 你的软件属于什么版本…

fork 与 vfork 的区别

关键区别一&#xff1a; vfork 直接使用父进程存储空间&#xff0c;不拷贝。 关键区别二&#xff1a; vfork保证子进程先运行,当子进程调用exit退出后&#xff0c;父进程才执行。 我们可以定义一个cnt&#xff0c;在子进程中让它变成3&#xff0c; 如果使用fork&#xff0c;那…

【LeetCode】【1】两数之和(1141字)

文章目录 [toc]题目描述样例输入输出与解释样例1样例2样例3 提示进阶Python实现哈希表 个人主页&#xff1a;丷从心 系列专栏&#xff1a;LeetCode 刷题指南&#xff1a;LeetCode刷题指南 题目描述 给定一个整数数组nums和一个整数目标值target&#xff0c;请在该数组中找出…

Typora+PicGo+Gitee设置图床,解决CSDN上传markdown文件图片加载不出来的问题(超级实用)

注&#xff1a; 由于gitee现在已经加上了防盗链&#xff0c;并且只支持1M的图片&#xff0c;我觉得不是很好用&#xff08;可以买腾讯云或阿里云等&#xff09;&#xff0c;之后找到比较好点的图床工具也会持续更新的。&#xff08;sm.ms好像还好&#xff0c;github网速不太稳定…

基于Vue和uni-app的增强型单选ccRadioView组件开发

标题&#xff1a;基于Vue和uni-app的增强单选组件ccRadioView的设计与实现 摘要&#xff1a;本文将详细介绍如何使用Vue和uni-app构建一个简单、好用且通用的单选框组件ccRadioView。该组件提供了单选列表的功能&#xff0c;并支持反向传值&#xff0c;方便开发者快速实现单选…

太速科技-基于5VLX110T FPGA FMC接口功能验证6U CPCI平台

基于5VLX110T FPGA FMC接口功能验证6U CPCI平台 一、板卡概述   本板卡是Xilinx公司芯片V5系列芯片设计信号处理板卡。由一片Xilinx公司的XC5VLX110T-1FF1136 / XC5VSX95T-1FF1136 / XC5VFX70T-1FF1136芯片组成。FPGA接1片DDR2内存条 2GB&#xff0c;32MB Nor flash存储器&a…

【Java基础】IO流(4) —— 转换流、打印流

【Java基础】IO流(1) —— 简介 【Java基础】IO流(2) —— 字符流 【Java基础】IO流(3) —— 字节流 【Java基础】IO流(4) —— 转换流、打印流 【Java基础】IO流(5) —— 序列流、内存流 【Java基础】IO流(6) —— 随机访问文件流、数据流 转换流 InputStreamReader 是字节输…

【制作100个unity游戏之28】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版4(附带项目源码)

最终效果 系列导航 文章目录 最终效果系列导航前言僵尸配置僵尸动画移动断头攻击死亡 源码完结 前言 本节主要实现配置僵尸动画&#xff0c;移动&#xff0c;断头&#xff0c;攻击&#xff0c;死亡功能。 僵尸 配置僵尸动画 普通动画 无头动画&#xff0c;新增覆盖图层 …

java实现环形链表带哨兵

双向环形链表带哨兵&#xff0c;这时哨兵既作为头&#xff0c;也作为尾。 package com.tfq.arithmetic.linkedlist;import java.util.Iterator;/*** author: fqtang* date: 2024/05/22/8:40* description: 环形链表*/ public class DoublyLinkedListSentinel implements Iterab…

嵌入式学习——3——IO分类模型

1、阻塞IO和非阻塞IO 1.1 阻塞IO - 在阻塞IO模型中&#xff0c;当一个IO操作&#xff08;如读取或写入&#xff09;开始时&#xff0c;如果数据没有准备好&#xff0c;程序会被挂起&#xff08;即阻塞&#xff09;&#xff0c;直到数据准备好并且IO操作完成。 - 在数据准备阶段…

浏览器API与协议

现代浏览器是一个囊括了数百个组件的操作系统&#xff0c;包括进程管理、安全沙箱、分层的优化缓存、JavaScript虚拟机、图形渲染和GPU管道、存储系统、传感器、音频和视频&#xff0c;网络机制等等。 在浏览器上运行的应用的性能。&#xff0c;取决于多个组件&#xff1a;解析…

notepad++ 批量转所有文件编码格式为UTF-8

1、安装notepad及PythonScript_3.0.18.0插件 建议两者都保持默认路径安装x64版本&#xff1a; 阿里云盘分享https://www.alipan.com/s/xVUDpY8v5QL安装好后如下图&#xff1a; 2、new Script&#xff0c;新建脚本&#xff0c;文件名为ConvertEncoding 3、自动打开脚本&#xff…

YOLOV8 如何训练自己的数据

1、git code 项目 地址 2、数据标注&#xff1a;使用yolov8官方推荐的roboflow 地址 2.1 上传数据 2.2 标注 2.3 生成数据集 2.4 导出数据 3 训练 3.1 建.yaml 文件 建立.yaml 文件 3.2 修改.yaml文件里面的内容 1.这是roboflow 网站下下来的数据&#xff0c;只需要把.…

MySQL中锁的几种类型

MySQL根据加锁的范围&#xff0c;可以分为全局锁、表级锁、行级锁三类。 2.5.1. 锁定读 2.5.1.1. 共享锁和独占锁 事务的 读-读 情况并不会引起什么问题&#xff0c;对于 写-写、读-写 或 写-读 这些情况可能会引起一些问题&#xff0c;需要使用MVCC或者加锁的方式来解决。在…

15:00面试,15:08就出来了,问的问题有点变态。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到8月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…

MySQL数据库下的Explain命令深度解析

Explain是一个非常有的命令&#xff0c;可以用来获取关于查询执行计划的信息&#xff0c;以及如何解释输出。Explain命令是查看查询优化器如何决定执行查询的主要方法。这个功能有一定的局限性&#xff0c;并不总是会说出真相&#xff0c;但是它的输出是可以获取的最好信息&…

Kubernetes集群上的Etcd备份和恢复

在本教程中&#xff0c;您将学习如何在Kubernetes集群上使用etcd快照进行etcd备份和恢复。 在Kubernetes架构中&#xff0c;etcd是集群的重要组成部分。所有集群对象及其状态都存储在etcd中。为了更好地理解Kubernetes&#xff0c;有几点关于etcd的信息是您需要了解的。 它是…

【探索数据结构】线性表之双链表

&#x1f389;&#x1f389;&#x1f389;欢迎莅临我的博客空间&#xff0c;我是池央&#xff0c;一个对C和数据结构怀有无限热忱的探索者。&#x1f64c; &#x1f338;&#x1f338;&#x1f338;这里是我分享C/C编程、数据结构应用的乐园✨ &#x1f388;&#x1f388;&…