数据结构(单向链表——c语言实现)

链式存储的优缺点:

优点:

1、动态分配内存:

链式存储不需要在数据插入之前分配固定大小的数组或内存块,因此它更适合存储动态变化的数据

2、高效的插入和删除操作:

在链表中插入或删除元素只需要调整相邻节点的指针,不需要移动大量数据,因此时间复杂度通常为O(1)(在已知位置时)或O(n)(在未知位置需要遍历链表时)。

3、节省内存:

链表中的每个节点只需要存储数据和指向下一个节点的指针,没有额外的空间浪费(如数组中的空闲空间)。

4、灵活性

链表可以以多种方式实现,如单向链表、双向链表、循环链表等,可以根据具体需求选择合适的结构。

缺点:

1、访问速度慢:

链表不支持像数组那样的随机访问,访问某个元素需要从头节点开始遍历,时间复杂度为O(n)。

2、额外的空间开销:

每个节点除了存储数据以外,还需要存储指针,增加了内存的开销。

3、内存碎片化:

链表中的节点可能在内存的不同位置分配内存,可能导致内存碎片化,影响性能。

4、管理复杂:

链表的管理(如内存分配和释放)相对复杂,容易出现内存泄露或指针错误等问题。

单向链表

单向链表(Singly Linked List)是一种链式存储结构的数据结构,它包含一系列节点(Node),每个节点都包含两个部分:数据域(存储节点的数据)和指针域(存储指向下一个节点的引用或指针)。单向链表的特点是链表中的节点只能单向遍历,即只能从链表的头部开始,依次访问每个节点,直到链表的尾部。

在我的链表中,我在定义时就给它一个规定:头节点不存数据,数据从头节点的下一个节点开始存储。

#ifndef _LINKLIST_H
#define _LINKLIST_H#include <stdio.h>
#include <stdlib.h>#define OUT(A) {printf("%c\n",A);}
typedef char Type;       //为了方便修改存储数据的类型//约定头节点不存数据
typedef struct node{Type data;         //节点中用于存储值struct node *next; //下一个节点的地址
}list;                 //声明了节点结构体类型//创建
list *create_link();
//判空
int null_link(list *l);
//求长度
int length_link(list *l);
//首插
void head_insert_link(list *l,Type data);
//尾插
void tail_insert_link(list *l,Type data);
//查找
list *search_link(list *l,Type data);
//更新
void updata_link(list *l,Type oldData,Type newData);
//删除
void delete_link(list *l,Type data);
//遍历
void traverse_link(list *l);
//初始化
void init_link(list *l);
//回收
void free_link(list **l);#endif

       还是跟顺序表一样,使用多文件封装的形式来存储,在.h文件中定义结构体,结构体包括存数据的变量data和指向下一个结构体的指针next;在这里定义结构体的时候node不能省略,因为在结构体里面需要定义一个指向这个结构体类型的指针,因此需要用到这个结构体类型来定义指针。

#include "linklist.h"//创建
list *create_link()
{list *l = (list *)malloc(sizeof(list));if(NULL == l){perror("create malloc");return NULL;}l->next = NULL;return l;
}
//判空
int null_link(list *l)
{if(NULL == l){puts("listlink is NULL");return -1;}return l->next == NULL?0:1;
}
//求长度
int length_link(list *l)
{if(NULL == l){puts("listlink is NULL");return -1;}int n = 0;while(l->next){n++;l = l->next;}return n;
}
//尾插
void tail_insert_link(list *l,Type data)
{if(NULL == l){puts("listlink is NULL");return;}//找表尾while(l->next) //l->next != NULL{l = l->next;}//创建新节点list *p=(list *)malloc(sizeof(list));if(NULL == l){perror("tail_insert malloc");}p->data = data; p->next = NULL;//把节点连接到链表中l->next = p;
}
//首插
void head_insert_link(list *l,Type data)
{if(NULL == l){puts("listlink is NULL");return;}list *p = (list *)malloc(sizeof(list));if(NULL == l){perror("head-insert malloc");return;}p->data = data;p->next = l->next;l->next = p;}
//查找
list *search_link(list *l,Type data)
{if(NULL == l){puts("listlink is NULL");return NULL;}while(l->next){/*if(l->next->data == data){return l->data;l = l->next;}*/l = l->next;if(data == l->data)return l;}return NULL;
}
//更新
void updata_link(list *l,Type oldData,Type newData)
{if(NULL == l){puts("listlink is NULL");return;}while(l->next){l = l->next;if(oldData == l->data){l->data = newData;}}  
}
//删除
void delete_link(list *l,Type data)
{if(NULL == l){puts("listlink is NULL");return;}
#if 1while(l->next){if(l->next->data == data){list *p = l->next;l->next = p->next;free(p);p = NULL;//continue;   //用else不用continue}else      l = l->next;}
#elif 0list *p = l;while(l->next){if(data != l->data)p = l;l = l->next;if(data == l->data){list *q = l;l = l->next;p->next = l;free(q);break;}}/*if(data == l->data) //注释break,加上最后的if为全删{p->next = NULL;free(l);}*/
#endif
}
//遍历
void traverse_link(list *l)
{
#if 0printf("head->");while(l->next){printf("%c->",l->next->data);l = l->next;}puts("NULL");
#elif 1list *p = l->next;while(p){printf("%c ",p->data);p = p->next;}puts("");
#endif
}
//初始化
void init_link(list *l)
{if(NULL == l){puts("listlink is NULL");return;}
#if 1while(l->next){list *p = l->next;l->next = p->next;free(p);}
#elselist *p = l->next;while(p){list *n = p;p = p->next;free(n);}l->next = NULL;
#endif
}
//回收
void free_link(list **l)
{if(NULL == *l){puts("listlink is NULL");return;}while((*l)->next){list *p = (*l)->next;(*l) = p->next;free(p);}*l = NULL;
}

创建:list *create_link()

       创建之后我们需要拿到头节点的地址,因此函数返回值为结构体指针类型,同样是在堆区开辟空间,因为约定了头节点不存数据,因此这里的data不用管,但是指向下一个节点的指针需要把它赋值为NULL,防止野指针出现。

判空:int null_link(list *l)

       因为链表中有指向下一个节点的指针,但是要是链表为空,那么就只有一个头节点,头节点里面的指针指向的是NULL,因此我们可以通过判断头节点里面的指针指向的下一个节点是否为空来判断链表是否为空;空函数返回0,非空返回1。

求长度:int length_link(list *l)

       单向链表的节点是通过指针连接在一起的,因此求长度只需要从头节点开始遍历到最后一个节点,遍历时使用一个变量计数,最后函数返回这个计数变量即可;循环条件为l->next,当循环到最后一个节点时,下一个为NULL,就会结束循环;在循环里每次让指针L往后移动一个节点。

尾插:void tail_insert_link(list *l,Type data)

       尾插也就是在链表最后插入一个新的节点,首先我们传参传的是链表的头节点,因此需要先遍历找到链表的最后一个节点,然后用一个结构体指针来接收新创建节点的地址,这里也需要判断一下节点创建是否成功,创建成功之后让节点的data等于我们要添加的值,然后让新节点里面的指针指向NULL,最后还需要更改插入新节点之前链表最后一个节点中指针的指向,让这个指针指向我们新加入的节点。

头部插入(首插):void head_insert_link(list *l,Type data)

       头插相比尾插要复杂一点,但是这里我们就不需要遍历链表了,因为我们拿到的参数就是头节点,首先也是需要创建新节点,然后给节点里的data赋值,然后让新节点里的指针next指向头节点的下一个,再让头节点的next指向我们新插入的节点;在这里头插函数中最后两条语句的顺序能更改,更改之后我们就无法正确将新节点的next指向头节点的下一个节点了。

查找:list *search_link(list *l,Type data)

        查找只需要循环遍历链表,然后将需要查找的数据与节点中的data比较即可,只要找到这个data,就将该data所在节点的地址返回就OK啦。

更新:void updata_link(list *l,Type oldData,Type newData)

       更新也就是将链表中某一个值修改为我们想改的值,传入的参数就有三个,表,原数据和更新的数据,也是通过循环遍历的方式来寻找我们需要更改的数据,找到之后直接修改即可,在代码中我的更新是将所有的要修改的那个原数据都改变,例如原来链表中有3个a,我要将a修改为b,那么就会将这三个a都修改为b,如果只想修改找到的第一个a的话就只需要在l->data = newData;语句后面加一个break,直接结束循环即可。

删除:void delete_link(list *l,Type data)

       删除和插入是类似的操作,只不过一个是增加节点,另一个是删除节点,首先是将要删除的数据当做参数传入函数,然后通过遍历的方式来查找需要删除的节点,找到节点之后用一个指针来指向这个节点,然后就是断链,将与它相连的前面一条链断开;在这里使用一个指针来指向这个节点是因为要把这个节点的空间释放掉,我们在找到这个节点之后就可以让它的前一个节点的next去指向要删除节点的下一个节点,再把要删除的节点空间释放掉,最后再让指针p指向NULL即可。在这里删除有删一个和删全部的区别,在代码中如果只想删除找到的第一个对应的字母,那就在p=NULL后语句之后加一个break结束循环即可。

遍历:void traverse_link(list *l)

        链表的遍历也就是通过循环来让L从头节点往后移动一直到最后一个节点,然后将每一个节点的data输出即可。 

初始化:void init_link(list *l)

       链表的初始化我们需要把除了头结点以外的节点都释放掉,同样还是使用循环遍历的方式,初始化跟删除是一样的操作,但是初始化是将所有节点都删除,跟删除差不多的我就不多说啦。

回收:void free_link(list **l)

       链表的回收就是先将链表初始化然后再把头节点也释放,最后再让指向头节点的指针指向空即可。

测试:(主函数)

#include "linklist.h"
int main(int agrc,char *agrv[])
{list *p = create_link(); tail_insert_link(p,'a');tail_insert_link(p,'b');tail_insert_link(p,'b');tail_insert_link(p,'f');printf("尾插后链表内容:");traverse_link(p);head_insert_link(p,'b');head_insert_link(p,'c');head_insert_link(p,'d');printf("头插后链表内容:");traverse_link(p);printf("此时链表长度为:%d\n",length_link(p));list *q = search_link(p,'d');OUT(q->data);q->data = 'a';printf("将查找到的d改为a后:");traverse_link(p);updata_link(p,'a','b');printf("将第一个a更新为b:");traverse_link(p);delete_link(p,'b');printf("删除第一个b后:");traverse_link(p);init_link(p);printf("此时链表长度为:%d\n",length_link(p));free_link(&p);printf("回收后p的指向:%p\n",p);return 0;
}

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

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

相关文章

基于Spring Boot的电子商务平台架构

2 相关技术 2.1 SpringBoot框架介绍 Spring Boot是一种不需要代码生成的一种框架&#xff0c;并且可以不需要配置任何的XML文件就可以&#xff0c;因为Spring Boot里面自带了很多接口&#xff0c;只需要配置不同的接口就会自动的应用并且识别需要的依赖&#xff0c;在配置方面非…

Level DB --- Block

class Block class Block是Level DB里面的重要数据结构&#xff0c;该数据结构用来承载已经存储到文件中的数据。已经被存储的数据当需要再次加载、应用&#xff08;例如搜索&#xff09;&#xff0c;这时首先要把数据加载、初始化到class Block里面。 数据的组织形式&#x…

记录大学Linux运维上机考试题目和流程

备注&#xff1a;今年的Linux操作系统考试已经全部结束&#xff0c;仅作为一个记录和留念 前提&#xff1a;配置环回网卡和环境和nat网卡 1、搭建dns服务器 2、Apache和http服务 3、搭建postfix邮件服务器实现邮件发送 4、搭建vsftpdFTP服务器实现文件上传 题目如下&…

前端面试笔试(四)

目录 一、数据结构算法等综合篇 1.线性探查法解决哈希冲突 2.请求分页系统中文件区和对换区 3.RADIUS认证协议&#xff0c;运行在哪个网络协议上 二、代码输出篇 1.res[1,2,100].map(parseInt) 如果我们想要输出为[1,2,100]&#xff0c;可以&#xff1a; 还可以换map里…

NVR录像机汇聚管理EasyNVR多品牌NVR管理工具视频汇聚技术在智慧安防监控中的应用与优势

随着信息技术的快速发展和数字化时代的到来&#xff0c;安防监控领域也在不断进行技术创新和突破。NVR管理平台EasyNVR作为视频汇聚技术的领先者&#xff0c;凭借其强大的视频处理、汇聚与融合能力&#xff0c;展现出了在安防监控领域巨大的应用潜力和价值。本文将详细介绍Easy…

C/C++运行库

文章目录 入口函数glibc入口函数_start__libc_start_mainMSVC入口函数堆初始化IO初始化 glibc C运行库glibc启动文件gcc补充C全局构造与析构 运行库对于多线程的改进线程局部存储 入口函数 使用C语言编写的一个hello world程序在用户看来的确非常简单&#xff0c;源代码仅需要…

学习使用LVGL,依赖官方网址

LVGL Basics — LVGL documentation LVGL基础知识 LVGL是一个开源的图形库&#xff0c;提供创建嵌入式GUI的一切 LVGL数据流 您为每个物理显示面板 创建一个显示器 (lv_display) &#xff0c;在其上创建屏幕小部件&#xff0c;将小部件添加到这些屏幕上。要处理触摸、鼠标、…

计算机网络HTTP——针对实习面试

目录 计算机网络HTTP什么是HTTP&#xff1f;HTTP和HTTPS有什么区别&#xff1f;分别说明HTTP/1.0、HTTP/2.0、HTTP/3.0请说明访问网页的全过程请说明HTTP常见的状态码Cookie和Session有什么区别&#xff1f;HTTP请求方式有哪些&#xff1f;请解释GET和POST的区别&#xff1f;HT…

大数据-226 离线数仓 - Flume 优化配置 自定义拦截器 拦截原理 了 拦截器实现 Java

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; Java篇开始了&#xff01; 目前开始更新 MyBatis&#xff0c;一起深入浅出&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff0…

无人机动力系统测试-实测数据与CFD模拟仿真数据关联对比分析

我们经常被问到这样的问题&#xff1a;“我们计划运行 CFD 仿真&#xff0c;我们还需要对电机和螺旋桨进行实验测试吗&#xff1f;我们可能有偏见&#xff0c;但我们的答案始终是肯定的&#xff0c;而且有充分的理由。我们自己执行了大量的 CFD 仿真&#xff0c;但我们承认&…

验证双随机矩阵(doubly stochastic matrix) 满足C(P)=C(P^T)

验证双随机矩阵(doubly stochastic matrix) 满足C( P P P)C(P T ^T T) 双随机矩阵&#xff1a; 在数学中&#xff0c;一个双随机矩阵&#xff08;doubly stochastic matrix&#xff09;是一个满足以下条件的矩阵&#xff1a; 非负矩阵&#xff1a;矩阵中的每个元素都是非负的…

Chrome 浏览器开启打印模式

打开开发者工具ctrl shift p输入print 找到 Emulate CSS print media type

Vite初始化Vue3+Typescrpt项目

初始化项目 安装 Vite 首先&#xff0c;确保你的 Node.js 版本 > 12.0.0。然后在命令行中运行以下命令来创建一个 Vite Vue 3 TypeScript 的项目模板&#xff1a; npm init vitelatest进入项目目录 创建完成后&#xff0c;进入项目目录&#xff1a; cd vue3-demo启动…

24 年第十届数维杯国际数模竞赛赛题浅析

本次万众瞩目的数维杯国际大学生数学建模赛题已正式出炉&#xff0c;无论是赛题难度还是认可度&#xff0c;该比赛都是数模届的独一档&#xff0c;含金量极高&#xff0c;可以用于综测加分、保研、简历添彩等各方面。考虑到大家解题实属不易&#xff0c;为了帮助大家取得好成绩…

自动语音识别(ASR)与文本转语音(TTS)技术的应用与发展

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

CentOS8 启动错误,enter emergency mode ,开机直接进入紧急救援模式,报错 Failed to mount /home 解决方法

先看现场问题截图&#xff1a; 1.根据提示 按 ctrld 输入 root 密码&#xff0c;进入系统。 2. 在紧急模式下运行&#xff1a;journalctl -xe &#xff0c;查看相关日志&#xff0c;找到关键点&#xff1a; Failed to mount /home 3.接着执行修复命令&#xff1a; xfs_repa…

Java项目实战II基于微信小程序的课堂助手(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 在数字化教…

借助 Pause 容器调试 Pod

借助 Pause 容器调试 Pod 在 K8S 中&#xff0c;Pod 是最核心、最基础的资源对象&#xff0c;也是 Kubernetes 中调度最小单元。在介绍 Pause 容器之前需要先说明下 Pod 与容器的关系来理解为什么需要 Pause 容器来帮助调试 1. Pod 与 容器的关系 Pod 是一个抽象的逻辑概念&…

IDEA自定义文件打开格式

介绍在IDEA中自定义文件打开格式的方法&#xff0c;比如一个文件&#xff0c;可以选择用txt格式打开&#xff0c;也可以选择用xml格式打开&#xff0c;也可以用java格式打开等等&#xff0c;通过这个方法可以方便的用任意格式在idea中打开想要打开的文件。 下面分别讨论三种不…