【数据结构】链表(1):单向链表和单向循环链表

链表

链表是一种经典的数据结构,它通过节点的指针将数据元素有序地链接在一起,在链表中,每个节点存储数据以及指向其他节点的指针(或引用)。链表具有动态性和灵活性的特点,适用于频繁插入、删除操作的场景。

定义

概念
  1. 将线性表 L = (a0,a1,...,an-1) 中各元素分布在存储器的不同存储块,称为结点。
    通过指针或地址建立起它们之间的联系,所得到的存储结构就是链表。
  2. 链表都有一个头结点(一般不保存数据,不做遍历),是链表的入口。
  3. 链表呈现一对一关系,且有一个前驱结点和一个后继结点。
  4. 每一个结点都往堆申请了地址,由多个堆组成。
分类
  1. 单向链表(重点)
  2. 单向循环链表
  3. 双向链表
  4. 双向循环链表(重点)
  5. 内核链表(重点)

我们这里将链表分成三篇文章来写,分别是 1:单向链表(重点)和单向循环链表;2:双向链表和双向循环链表(重点);3:内核链表(重点)。因为代码片如若过多可能会导致思维混乱,所以分开来写,将单向链表和循环链表放在一篇文章之中是因为二者的理念相同,只是首位相连接,代码逻辑接近,方便理解和对比。

链表的优缺点(想要具体了解链表和顺序表之间的区别详细,请查阅这篇文章:<链接:【数据结构】顺序表和链表优劣的对比分析>)

  • 优点:插入和删除非常方便。
  • 缺点:查找和替换比较麻烦。
  • 操作:对链表中数据的增删改查 —> 大原则:先连后断
单向链表(Singly Linked List)

定义:

  • 每个节点包含两部分:
    • 数据域(存储数据)。
    • 指针域(存储指向下一个节点的指针)。
  • 只有一个方向,从头节点开始依次访问每个节点。

特点:

  • 插入和删除操作高效,不需要移动其他节点。
  • 无法直接访问某个特定位置的元素,需要从头节点开始遍历。
#define datatype int
typedef struct link{datatype data;           //数据域  struct link *next;  //指向后继结点的指针域
}link_t;// 缺点:只能从头结点一直往后走// 头插:头结点(最前面的结点)后面插入
// 尾插:尾结点(最后面的结点)后面插入
1> 初始化link_init
link_t *link_init(void)  //造头结点
{//1>向堆申请link_t *p = (link_t *)malloc(sizeof(link_t));if(NULL == p){perror("malloc");return NULL;}//将指针赋值 ,为了安全指向NULLp->next = NULL;return p;
}
2> 创建结点create_node(static)
static link_t *create_node(datatype d)
{//1>向堆空间申请link_t *p = (link_t *)malloc(sizeof(link_t));if(NULL == p){perror("malloc");return NULL;}//2>赋值p->data = d;p->next = NULL;return p;
}

在这里插入图片描述

3> 插入函数insert_behind(static)
//将一个结点(a)插到另一个结点(b)的后面
static void insert_behind(link_t *a,link_t *b)
{//遵循先连后断 a->next = b->next;  //避免b指向的地址丢失b->next = a;
}
4> 头插函数insert_head
void insert_head(link_t *p,datatype d)
{//利用传进来的数据,调用创建结点函数link_t *node = create_node(d);//将创建出来的node结点插到头结点后面insert_behind(node,p);  
}

在这里插入图片描述

5> 遍历展示display
void display(link_t *head)
{//遍历整个链表while(head->next != NULL){//将head指针变量往右移head = head->next;printf("%d ",head->data); } printf("\n");
}
6> 尾插函数insert_tail
//每插入一个结点进来,将其插入到尾结点后面
void insert_tail(link_t *p,datatype data)
{link_t *node = create_node(data);if(NULL == node)return;//找到尾结点 while(p->next != NULL){p = p->next;}insert_behind(node,p);  
}

在这里插入图片描述

7> 删除函数link_del
void link_del(link_t *p,datatype d)
{link_t *node = NULL;//遍历while(p->next != NULL){//进行数据对比if(p->next->data == d) //从头结点后第一个有数据的结点开始判断{//先保存要删除结点的地址node = p->next;// 将p的next指向node的nextp->next = node->next;//为了安全,在释放前,令它的指针域指向NULLnode->next = NULL;//释放free(node);continue;}//往后继续遍历,找相同数据的结点p = p->next; }}

在这里插入图片描述

8> 修改函数link_replace
void link_replace(link_t *p,int old,int new)
{//遍历找到旧数据所在的地址while(p->next != NULL){if(p->next->data == old){p->next->data = new;continue; } p = p->next;}
}
完整代码(共三个文件:头文件、链表函数和主函数)

头文件: link.h

#ifndef __LINK_H__
#define __LINK_H__#include <stdio.h>
#include <stdlib.h>#define datatype inttypedef struct link{datatype data;           //数据域  struct link *next;  //指向后继结点的指针域
}link_t;extern link_t *link_init(void);
extern void insert_head(link_t *p,datatype d);
extern void display(link_t *head);
extern void insert_tail(link_t *p,datatype data);
extern void link_del(link_t *p,datatype d);
extern void link_replace(link_t *p,int old,int new);#endif

链表函数:link.c

#include "link.h"
//初始化
link_t *link_init(void)  //造头结点
{//1>向堆申请link_t *p = (link_t *)malloc(sizeof(link_t));if(NULL == p){perror("malloc");return NULL;}//将指针赋值 ,为了安全指向NULLp->next = NULL;return p;
}//创建结点
static link_t *create_node(datatype d)
{//1>向堆空间申请link_t *p = (link_t *)malloc(sizeof(link_t));if(NULL == p){perror("malloc");return NULL;}//2>赋值p->data = d;p->next = NULL;return p;
}//插入函数insert_behind
//将一个结点(a)插到另一个结点(b)的后面
static void insert_behind(link_t *a,link_t *b)
{//遵循先连后断 a->next = b->next;  //避免b指向的地址丢失b->next = a;
}//头插函数insert_head
void insert_head(link_t *p,datatype d)
{//利用传进来的数据,调用创建结点函数link_t *node = create_node(d);//将创建出来的node结点插到头结点后面insert_behind(node,p); 
}//遍历操作
void display(link_t *head)
{//遍历整个链表while(head->next != NULL){//将head指针变量往右移head = head->next;printf("%d ",head->data); } printf("\n");
}//每插入一个结点进来,将其插入到尾结点后面
void insert_tail(link_t *p,datatype data)
{link_t *node = create_node(data);if(NULL == node)return;//找到尾结点 while(p->next != NULL){p = p->next;}insert_behind(node,p);  
}//删除
void link_del(link_t *p,datatype d)
{link_t *node = NULL;//遍历while(p->next != NULL){//进行数据对比if(p->next->data == d) //从头结点后第一个有数据的结点开始判断{//先保存要删除结点的地址node = p->next;// 将p的next指向node的nextp->next = node->next;//为了安全,在释放前,令它的指针域指向NULLnode->next = NULL;//释放free(node);continue;}//往后继续遍历,找相同数据的结点p = p->next; }
}//替换
void link_replace(link_t *p,int old,int new)
{//遍历找到旧数据所在的地址while(p->next != NULL){if(p->next->data == old){p->next->data = new;continue; } p = p->next;}
}

主函数:main.c

#include "link.h"int main(void)
{link_t *head = link_init();if(NULL == head)return -1;printf("%p\n",head);int data,ret;while(1){printf("请开始头插\n");ret = scanf("%d",&data);if(ret == 0)break;insert_head(head,data); display(head);}getchar();while(1){printf("请开始尾插\n");ret = scanf("%d",&data);if(ret == 0)break;insert_tail(head,data); display(head);}getchar();while(1){printf("请开始删除\n");ret = scanf("%d",&data);if(ret == 0)break;link_del(head,data); display(head);}getchar();int old,new;while(1){printf("请开始替换\n");ret = scanf("%d%d",&old,&new);if(ret == 0)break;link_replace(head,old,new); display(head);}return 0;
}
单向循环链表(Singly Circular Linked List)

定义:

  • 单向链表的变体,最后一个节点的指针指向头节点,形成一个环。
  • 从链表的任何节点出发,都可以遍历整个链表。

特点:

  • 无需额外存储头尾信息,适合循环任务。
  • 需要注意防止死循环。
#define datatype int
typedef struct link{datatype data;           //数据域  struct link *next;      //指向后继结点的指针域
}link_t;// 头插:头结点(最前面的结点)后面插入
// 尾插:尾结点(最后面的结点)后面插入
1> 初始化link_init
link_t *link_init(void)  //造头结点
{//1>向堆申请link_t *p = (link_t *)malloc(sizeof(link_t));if(NULL == p){perror("malloc");return NULL;}//将指针赋值 ,为了安全指向NULL  //修改处p->next = p; //自己指向自己return p;
}
2> 创建结点create_node
static link_t *create_node(datatype d)
{//1>向堆空间申请link_t *p = (link_t *)malloc(sizeof(link_t));if(NULL == p){perror("malloc");return NULL;}//2>赋值  //修改处p->data = d;p->next = p;   //指向自己return p;
}
3> 插入函数insert_behind(static)
//将一个结点(a)插到另一个结点(b)的后面
static void insert_behind(link_t *a,link_t *b)
{//遵循先连后断 a->next = b->next;  //避免b指向的地址丢失b->next = a;
}
4> 头插函数insert_head
void insert_head(link_t *p,datatype d)
{//利用传进来的数据,调用创建结点函数link_t *node = create_node(d);//将创建出来的node结点插到头结点后面insert_behind(node,p); 
}
5> 遍历展示display
void display(link_t *head)
{//修改处link_t *p = head;//遍历整个链表  //修改处while(head->next != p)  //最后一个结点指向的是头结点{//将head指针变量往右移head = head->next;printf("%d ",head->data); } printf("\n");
}
6> 尾插函数insert_tail
//每插入一个结点进来,将其插入到尾结点后面
void insert_tail(link_t *p,datatype data)
{//修改处link_t *head = p; link_t *node = create_node(data);if(NULL == node)return;//找到尾结点  //修改处while(p->next != head)  //尾结点指向头结点{p = p->next;}insert_behind(node,p);  
}
7> 删除函数link_del
void link_del(link_t *p,datatype d)
{//修改处,保存头结点的地址link_t *head = p;link_t *node = NULL;//遍历   //修改处while(p->next != head){//进行数据对比if(p->next->data == d) //从头结点后第一个有数据的结点开始判断{//先保存要删除结点的地址node = p->next;// 将p的next指向node的nextp->next = node->next;//为了安全,在释放前,令它的指针域指向自己  //修改处node->next = node;//释放free(node);continue;}//往后继续遍历,找相同数据的结点p = p->next; }}
8> 修改函数link_replace
void link_replace(link_t *p,int old,int new)
{//修改处,保存头结点地址link_t *head = p;//遍历找到旧数据所在的地址  修改处while(p->next != head){if(p->next->data == old){p->next->data = new;continue; } p = p->next;}
}
完整代码(共三个文件:头文件、链表函数和主函数)

头文件: cyclelink.h

#ifndef __CYCLELINK_H__ 
#define __CYCLELINK_H__#include <stdio.h>
#include <stdlib.h>#define datatype int
typedef struct link{datatype data;           //数据域  struct link *next;  //指向后继结点的指针域
}link_t;extern link_t *link_init(void);
extern void insert_head(link_t *p,datatype d);
extern void display(link_t *head);
extern void insert_tail(link_t *p,datatype data);
extern void link_del(link_t *p,datatype d);
extern void link_replace(link_t *p,int old,int new);#endif

链表函数: cyclelink.c

#include "cyclelink.h"link_t *link_init(void)  //造头结点
{//1>向堆申请link_t *p = (link_t *)malloc(sizeof(link_t));if(NULL == p){perror("malloc");return NULL;}//将指针赋值 ,为了安全指向NULL  //修改处**p->next = p; //自己指向自己return p;
}static link_t *create_node(datatype d)
{//1>向堆空间申请link_t *p = (link_t *)malloc(sizeof(link_t));if(NULL == p){perror("malloc");return NULL;}//2>赋值  //修改处p->data = d;p->next = p;   //指向自己return p;
}//将一个结点(a)插到另一个结点(b)的后面
static void insert_behind(link_t *a,link_t *b)
{//遵循先连后断 a->next = b->next;  //避免b指向的地址丢失b->next = a;
}void insert_head(link_t *p,datatype d)
{//利用传进来的数据,调用创建结点函数link_t *node = create_node(d);//将创建出来的node结点插到头结点后面insert_behind(node,p); 
}void display(link_t *head)
{//修改处link_t *p = head;//遍历整个链表  //修改处while(head->next != p)  //最后一个结点指向的是头结点{//将head指针变量往右移head = head->next;printf("%d ",head->data); } printf("\n");
}//每插入一个结点进来,将其插入到尾结点后面
void insert_tail(link_t *p,datatype data)
{//修改处link_t *head = p;link_t *node = create_node(data);if(NULL == node)return;//找到尾结点  //修改处while(p->next != head)  //尾结点指向头结点{p = p->next;}insert_behind(node,p);  
}void link_del(link_t *p,datatype d)
{//修改处,保存头结点的地址link_t *head = p;link_t *node = NULL;//遍历   //修改处while(p->next != head){//进行数据对比if(p->next->data == d) //从头结点后第一个有数据的结点开始判断{//先保存要删除结点的地址node = p->next;// 将p的next指向node的nextp->next = node->next;//为了安全,在释放前,令它的指针域指向自己  //修改处node->next = node;//释放free(node);continue;}//往后继续遍历,找相同数据的结点p = p->next; }}void link_replace(link_t *p,int old,int new)
{//修改处,保存头结点地址link_t *head = p;//遍历找到旧数据所在的地址  修改处while(p->next != head){if(p->next->data == old){p->next->data = new;continue; } p = p->next;}
}

主函数:main.c

#include "cyclelink.h"int main(void)
{link_t *head = link_init();if(NULL == head)return -1;printf("%p\n",head);int data,ret;while(1){printf("请开始头插\n");ret = scanf("%d",&data);if(ret == 0)break;insert_head(head,data); display(head);}getchar();while(1){printf("请开始尾插\n");ret = scanf("%d",&data);if(ret == 0)break;insert_tail(head,data); display(head);}getchar();while(1){printf("请开始删除\n");ret = scanf("%d",&data);if(ret == 0)break;link_del(head,data); display(head);}getchar();int old,new;while(1){printf("请开始替换\n");ret = scanf("%d%d",&old,&new);if(ret == 0)break;link_replace(head,old,new); display(head);}return 0;
}

综上。希望该内容能对你有帮助,感谢!

以上。仅供学习与分享交流,请勿用于商业用途!转载需提前说明。

我是一个十分热爱技术的程序员,希望这篇文章能够对您有帮助,也希望认识更多热爱程序开发的小伙伴。
感谢!

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

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

相关文章

离散数学考前一天

判断强连通&#xff0c;单向连通&#xff0c;弱连通&#xff1a; 求可达性矩阵P&#xff0c;P里面全是1&#xff0c;就是强连通 否则看P与P的转置矩阵&#xff0c;如果除了主对角线是0&#xff0c;其他全是1&#xff0c;就是单向连通 否则看A1&#xff1d;A与A的转置矩阵&am…

【服务器项目部署】⭐️将本地项目部署到服务器!

目录 &#x1f378;前言 &#x1f37b;一、服务器选择 &#x1f379; 二、服务器环境部署 2.1 java 环境部署 2.2 mysql 环境部署 &#x1f378;三、项目部署 3.1 静态页面调整 3.2 服务器端口开放 3.3 项目部署 ​ &#x1f379;四、测试 &#x1f378;前言 小伙伴们大家好…

chrome缓存机制以及验证缓存机制

一、Chrome 缓存机制 浏览器缓存机制旨在提高网页加载速度、减少服务器负载和节约带宽。Chrome 的缓存主要包括以下几种类型&#xff1a; 1. 强缓存 (Strong Cache) 无需向服务器发送请求即可使用缓存的资源。由 HTTP 响应头控制&#xff0c;包括&#xff1a; Expires&…

西门子DBX DBD DBB DBW的关系

DB10.DBD0 DB10.DBW0DB10.DBW2 DB10.DBB0DB10.DBB1DB10.DBB2DB10.DBB3 DB10.DBX0.00.7DB10.DBX1.01.7DB10.DBX2.02.7DB10.DBX3.03.7 使用之前需要在DB10中先定义&#xff0c;如果你仅在DB10中定义了一个DBD0&#xff0c;那么原则上你是可以使用上述所有地址的&#xff0c;但…

Android `android.graphics` 包深度解析:架构与设计模式

Android android.graphics 包深度解析:架构与设计模式 目录 引言android.graphics 包概述核心类与架构 CanvasPaintBitmapColorPathShaderMatrix设计模式在 android.graphics 中的应用 工厂模式装饰者模式策略模式享元模式高级图形处理技术 硬件加速离屏渲染自定义 View 中的…

Nginx的性能分析与调优简介

Nginx的性能分析与调优简介 一、Nginx的用途二、Nginx负载均衡策略介绍与调优三、其他调优方式简介四、Nginx的性能监控 一、Nginx的用途 ‌Nginx是一种高性能的HTTP和反向代理服务器&#xff0c;最初作为HTTP服务器开发&#xff0c;主要用于服务静态内容如HTML文件、图像、视…

vue2使用pdfjs-dist和jsPDF生成pdf文件

vue2使用pdfjs-dist和jsPDF生成pdf文件 1、安装依赖 npm install pdfjs-dist2.6.3472、引入依赖 import { jsPDF } from jspdf// 使用require方式导入pdfjs-dist v2.6.347&#xff0c;高版本报错&#xff08;import导入会报错&#xff1a;GlobalWorkerOptions undefined&…

sklearn_pandas.DataFrameMapper的用法

文章目录 介绍主要作用基本用法示例对不同列应用不同的转换器对多列应用相同的转换器输出为 Pandas DataFrame 注意事项转换器的适用性&#xff1a;输出格式&#xff1a;与 scikit-learn 的兼容性&#xff1a; 介绍 DataFrameMapper 是 sklearn-pandas 库中的一个工具&#xf…

HTML——31.定义媒介资源

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>定义媒介资源</title></head><body><!--source标签用来为<video>视频和<audio>音频 &#xff0c;定义媒介资源--><!--src属性&…

宝塔-firefox(Docker应用)-构建自己的Web浏览器

安装基础软件 宝塔中安装firefox(Docker应用) 。宝塔中需要先安装docker及docker-composefirefox配置安装 点击firefox应用&#xff0c;选择【安装配置】点击右边绿色按钮&#xff0c;进行安装&#xff0c;这一步等待docker-compose根据你的配置初始化docker应用 等待安装 …

ArcGIS土地利用数据制备、分析及基于FLUS模型土地利用预测(数据采集、处理、分析、制图)

FLUS&#xff08;Flexible Land Use Simulation&#xff09;模型是一个用于模拟土地利用变化的模型&#xff0c;它结合了经济理论、土地利用和土地覆盖变化的动态过程。FLUS模型由美国农业部农业经济研究服务局&#xff08;ERS&#xff09;开发&#xff0c;旨在提供对美国及全球…

【文献精读笔记】Explainability for Large Language Models: A Survey (大语言模型的可解释性综述)(一)

****非斜体正文为原文献内容&#xff08;也包含笔者的补充&#xff09;&#xff0c;灰色块中是对文章细节的进一步详细解释&#xff01; 三、传统微调范式&#xff08;Traditional Fine-Tuning Paradigm&#xff09; 在这个范式中&#xff0c;首先在大量未标记的文本数据上预…

【泰克生物】从酵母细胞表面展示到抗体筛选:实现精准药物发现

在现代药物发现领域&#xff0c;精准筛选和优化抗体已成为一种必不可少的技术手段。传统的抗体筛选方法依赖于动物免疫或体外筛选&#xff0c;这些方法往往成本高且周期长。近年来&#xff0c;酵母细胞表面展示技术&#xff08;Yeast Surface Display, YSD&#xff09;成为一种…

C++ 设计模式:门面模式(Facade Pattern)

链接&#xff1a;C 设计模式 链接&#xff1a;C 设计模式 - 代理模式 链接&#xff1a;C 设计模式 - 中介者 链接&#xff1a;C 设计模式 - 适配器 门面模式&#xff08;Facade Pattern&#xff09;是一种结构型设计模式&#xff0c;它为子系统中的一组接口提供一个一致&#…

基于YOLOV5+Flask安全帽RTSP视频流实时目标检测

1、背景 在现代工业和建筑行业中&#xff0c;安全始终是首要考虑的因素之一。特别是在施工现场&#xff0c;工人佩戴安全帽是确保人身安全的基本要求。然而&#xff0c;人工监督难免会有疏漏&#xff0c;尤其是在大型工地或复杂环境中&#xff0c;确保每个人都佩戴安全帽变得非…

【每日学点鸿蒙知识】深色模式、Webview查看版本、window设置亮度、List缓存节点更新、预编译JS

1、HarmonyOS 深色模式下canvas绘制通过resourcemanager.getColor不是实际dark模式下的颜色&#xff1f; 深色模式下canvas绘制通过resourcemanager.getColor不是实际dark模式下的颜色 正确获取到资源resourcemanager. getColorSync($r(‘app.color.test_color’).id);深色模…

Day08:字符串

1. 什么是字符串 字符串&#xff08;String&#xff09; 是由一系列字符组成的文本数据。在Python中&#xff0c;字符串用引号括起来&#xff0c;既可以是单引号&#xff0c;也可以是双引号"。 字符串可以包含字母、数字、空格、符号等内容。 示例&#xff1a;定义字符串…

Unity开发微信小游戏踩坑总结

前言 不记录真记不住&#xff0c;这个帖子以后不定时更新。 问题1&#xff1a;图片模糊 问题描述&#xff1a; 在Unity里什么事没有&#xff0c;进入到微信开发者工具里就已经模糊了&#xff0c;人物动画是一团马赛克&#xff0c;图片看着倒是没事。 问题原因&#xff1a;…

Vue异步处理、异步请求

Vue.js 是一个用于构建用户界面的渐进式JavaScript框架&#xff0c;它允许开发者以声明式的方式编写异步操作。在 Vue 中处理异步任务非常重要&#xff0c;因为很多现代Web应用都需要与后端API交互、执行耗时计算或管理复杂的异步流程。以下是对 Vue 中异步处理的详细讲解&…

【AI日记】24.12.30 kaggle 比赛 2-18

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】 工作 参加&#xff1a;kaggle 比赛 Regression with an Insurance Dataset时间&#xff1a;8 小时 读书 1 书名&#xff1a;教育的本质时间&#xff1a;0.5 小时评估&#xff1a;快速读完&#xff0c;收获不…