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

server/2024/11/20 17:06:36/

链式存储的优缺点:

优点:

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.ppmy.cn/server/143504.html

相关文章

昇腾系列双处理边缘计算盒子DA500I,打造高效低延迟的视觉推理解决方案

随着深度学习模型在机器视觉领域的持续优化&#xff0c;目标检测、识别和分类能力显著提升&#xff0c;对计算硬件提出了更高要求。深度学习任务需要大量计算资源&#xff0c;特别是在边缘设备上&#xff0c;单一处理器盒子如CPU在处理矩阵运算和图像分析时效率较低&#xff0c…

初始Python篇(6)—— 字符串

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a; Python 目录 字符串的常见操作 格式化字符串 占位符 f-string 字符串的 format 方法 字符串的编码与解码 与数据验证相关的方法 …

基于YOLOv8深度学习的智慧城市管理井盖状态检测系统(PyQt5界面+数据集+训练代码)

本研究设计并实现了一种基于YOLOv8深度学习的智慧城市管理井盖状态检测系统&#xff0c;旨在提高城市井盖管理的效率与安全性&#xff0c;减少因井盖缺失或损坏而可能带来的安全隐患。井盖作为城市基础设施的重要组成部分&#xff0c;其状态直接关系到行人和车辆的安全。传统的…

C# 超链接控件LinkLabel无法触发Alt快捷键

在C#中&#xff0c;为控件添加快捷键的方式有两种&#xff0c;其中一种就是Windows中较为常见的Alt快捷键&#xff0c;比如运行对话框&#xff0c;记事本菜单等。只需要按下 Alt 框号中带下划线的字母即可触发该控件的点击操作。如图所示 在C#开发中&#xff0c;实现类似的操作…

[开源重构]Search(Elasticsearch/OpenSearch) Sync Tool

[开源重构]Elasticsearch/OpenSearch Sync Tool 背景 因为要做集群灾备,需要在主备两个集群之间持续性地同步数据,调查过多个方案: CCR(Cross-cluster replication) 官方工具,可惜需要收费,无奈放弃 &#x1f626;esm 如官方文档说所,最大的特点快. 可惜也发现不少问题&#…

Jmeter的后置处理器(二)

5--JSR223 PostProcessor 功能特点 自定义后处理逻辑&#xff1a;使用脚本语言编写自定义的后处理逻辑。支持多种脚本语言&#xff1a;支持 Groovy、JavaScript、BeanShell 等脚本语言。动态参数传递&#xff1a;将提取的数据存储为变量&#xff0c;供后续请求使用。灵活性高…

在windows上打包mediasoup arm64版本的docker镜像

mediasoup版本&#xff1a;3.14.14 mediasoup-demo版本&#xff1a;v3 windows 10 专业版 docker-desktop版本&#xff1a;4.30.0 (149282) docker info: Client:Version: 26.1.1Plugins:buildx: Docker Buildx (Docker Inc.)Version: v0.14.0-desktop.1Path: C:\Prog…

Linux-Apache

文章目录 Apache基础配置 &#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;Linux专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年11月19日12点20分 Apache Web服务器用来实现HTTP和相关TCP连接的处理&#xff0c;同时负责所提供资源的管理…