【数据结构】单链表的使用
单链表的使用
- 1、基本概念
- 2、链表的分类
- 3、链表的基本操作
- a、单链表节点设计
- b、单链表初始化
- c、单链表增删节点
- **节点头插:**
- **节点尾插:**
- **新节点插入指定节点后:**
- 节点删除:
- d、单链表修改节点
- e、单链表遍历,并打印数据
- 4、链表优缺点
- 5、完整示例代码
1、基本概念
- 顺序表:顺序存储的线性表。
- 链式表:链式存储的线性表,简称链表。
既然顺序存储中的数据因为挤在一起而导致需要成片移动,那很容易想到的解决方案是将数据离散地存储在不同内存块中,然后在用来指针将它们串起来。这种朴素的思路所形成的链式线性表,就是所谓的链表。
顺序表和链表在内存在的基本样态如下图所示:
- 顺序表:
链表:
2、链表的分类
\quad 根据链表中各个节点之间使用指针的个数,以及首尾节点是否相连,可以将链表细分为如下种类:
1.单向链表
2.单向循环链表
3.双向循环链表
\quad 这些不同链表的操作都是差不多的,只是指针数目的异同。以最简单的单向链表为例,其基本示意图如下所示:
上图中,所有的节点均保存一个指针,指向其逻辑上相邻的下一个节点(末尾节点指向空)。
另外注意到,整条链表用一个所谓的头指针 head 来指向,由 head 开始可以找到链表中的任意一个节点。head 通常被称为头指针
。
3、链表的基本操作
- 节点设计
- 初始化空链表
- 增删节点
- 链表遍历(改查)
- 销毁链表
下面着重针对这几项常见操作,讲解单向链表的处理。
a、单链表节点设计
单向链表的节点非常简单,节点中除了要保存用户数据之外(这里以整型数据为例),只需要增加一个指向本类节点的指针即可,如下所示:
typedef struct single_list{// 数据域--》真实的数据int data; // 以整型数据为例// 指针域struct single_list *next; // //用来指向下一个元素在内存中的首地址
}single_list_t;
b、单链表初始化
\quad 首先,空链表有两种常见的形式。一种是带头结点的,一种是不带头结点的。所谓的头结点是不存放有效数据的节点,仅仅用来方便操作,如下:
而不带头结点的空链表如下所示:
\quad 由于头结点是不存放有效数据的,因此如果空链表中带有头结点,那么头指针 head 将永远不变,这会给以后的链表操作带来些许便捷。
下面以带头结点的链表为例,展示单向链表的初始化的示例代码:
single_list_t *single_list_init()
{single_list_t *head_node = malloc(sizeof(single_list_t));if (head_node != NULL){head_node->next = NULL;}return head_node;
}
c、单链表增删节点
\quad 相对于顺序表需要整片移动数据,链表增删节点只需要修改几个相关指针的指向,动作非常快速。
\quad 与顺序表类似,可以对一条链表中的任意节点进行增删操作,示例代码:
节点头插:
int insert_list_head(int newdata, single_list_t *list)
{// 申请一个节点 -堆空间single_list_t *new_node = malloc(sizeof(single_list_t));// 初始化数据域new_node->data = newdata;new_node->next = NULL;// 插入节点new_node->next = list->next;list->next = new_node;return 0;
}
节点尾插:
int insert_list(int newdata, single_list_t *list)
{// 申请一个节点 -堆空间single_list_t *new_node = malloc(sizeof(single_list_t));// 初始化数据域new_node->data = newdata;new_node->next = NULL;// 定义指针p去找到链表的尾部single_list_t *p = list;while (p->next != NULL){p = p->next;}// 此时p已经到最后一个节点的位置p->next = new_node;return 0;
}
新节点插入指定节点后:
int insert_list_mid(int olddata, int newdata, single_list_t *list)
{// 找到要插入的节点single_list_t *p = list;while (p->next != NULL){p = p->next;if (p->data == olddata) // 待插入的节点在链表中间{break;}}// 申请一个新的节点 single_list_t *new_node = malloc(sizeof(single_list_t));// 初始化数据域new_node->data = newdata;new_node->next = NULL;// p指向最后一个节点if (p->next == NULL){// 最后一个节点是要插入的节点if (p->data == olddata){new_node->next = p->next; // 先让新增加的节点指向找到节点的下一个节点p->next = new_node; // 再将该节点指向新增加的节点}else{printf("要插入的数据不存在\n");return -1;}}else // 遍历到中间找到需要插入的节点{new_node->next = p->next; // 先让新增加的节点指向找到节点的下一个节点p->next = new_node; // 再将该节点指向新增加的节点}return 0;
}
节点删除:
int list_delnode(int deldata, single_list_t *list)
{single_list_t *q = list;single_list_t *p = list->next;while (p->next != NULL){// 找到要删除的节点并进行删除if (p->data == deldata){q->next = p->next; // 将q指向的节点指向被删除节点的下一个节点p->next = NULL; // p节点的next指针指向NULLfree(p); // 释放p后此时p是野指针p = q->next;// 将p指向被删除节点的下一个节点}else{p = p->next;q = q->next;} }// 遍历到最后一个节点if (p->next == NULL){// 若最后一个节点是要删除的节点,则删除if (p->data == deldata){q->next = p->next;p->next = NULL;free(p);}else{printf("最后一个节点不是要删除的节点\n");return 0;}}
}
d、单链表修改节点
int list_update_node(int old_data, int new_data, single_list_t *list)
{single_list_t *p = list;while (p->next != NULL){p = p->next; // p指向下一个节点if (p->data == old_data){p->data = new_data;}}return 0;
}
e、单链表遍历,并打印数据
int list_show(single_list_t *list)
{single_list_t *p = list;while (p->next != NULL){p = p->next;printf("当前p指向的节点数据:%d\n", p->data);}
}
4、链表优缺点
\quad 链式存储中,所有节点的存储位置是随机的,他们之间的逻辑关系用指针来确定,跟物理存储位置无关,因此从上述示例代码可以很清楚看到,增删数据都非常迅速,不需要移动任何数据。另外,又由于位置与逻辑关系无关,因此也无法直接访问某一个指定的节点,只能从头到尾按遍历的方式一个个找到想要的节点。简单讲,链式存储的优缺点跟顺序存储几乎是相对的。
总结其特点如下:
- 优点
a.插入、删除时只需要调整几个指针,无需移动任何数据
b.当数据节点数量较多时,无需一整片较大的连续内存空间,可以灵活利用离散的内存
c.当数据节点数量变化剧烈时,内存的释放和分配灵活,速度快 - 缺点
a.在节点中,需要多余的指针来记录节点之间的关联
b.所有数据都是随机存储的,不支持立即访问任意一个随机数据。
5、完整示例代码
#include <stdio.h>
#include <stdlib.h>// 1.封装一个结构体来表示单链表
typedef struct single_list{int data;struct single_list *next;
}single_list_t;// 2.初始化单链表-->定义结构体变量 创建头节点
single_list_t *single_list_init()
{single_list_t *head_node = malloc(sizeof(single_list_t));if (head_node != NULL){head_node->next = NULL;}return head_node;
}// 头插
int insert_list_head(int newdata, single_list_t *list)
{// 申请一个节点 -堆空间single_list_t *new_node = malloc(sizeof(single_list_t));// 初始化数据域new_node->data = newdata;new_node->next = NULL;// 插入节点new_node->next = list->next;list->next = new_node;return 0;
}
/*@brief:3.插入数据-->尾插(在最后一个有效成员的后面插入数据)*@param(in): newdata :待插入的数据 list:待插入的链表*@param(out): *@retval:
*/
int insert_list(int newdata, single_list_t *list)
{// 申请一个节点 -堆空间single_list_t *new_node = malloc(sizeof(single_list_t));// 初始化数据域new_node->data = newdata;new_node->next = NULL;// 定义指针p去找到链表的尾部single_list_t *p = list;while (p->next != NULL){p = p->next;}// 此时p已经到最后一个节点的位置p->next = new_node;return 0;
}// 中间插入
int insert_list_mid(int olddata, int newdata, single_list_t *list)
{// 找到要插入的节点single_list_t *p = list;while (p->next != NULL){p = p->next;if (p->data == olddata){break;}}// 申请一个节点 -堆空间single_list_t *new_node = malloc(sizeof(single_list_t));// 初始化数据域new_node->data = newdata;new_node->next = NULL;// p指向最后一个节点if (p->next == NULL){if (p->data == olddata){new_node->next = p->next; // 先让新增加的节点指向找到节点的下一个节点p->next = new_node; // 再将该节点指向新增加的节点}else{printf("要插入的数据不存在\n");return -1;}}else // 遍历到中间找到需要插入的节点{new_node->next = p->next; // 先让新增加的节点指向找到节点的下一个节点p->next = new_node; // 再将该节点指向新增加的节点}return 0;
}
// 删除节点
int list_delnode(int deldata, single_list_t *list)
{single_list_t *q = list;single_list_t *p = list->next;while (p->next != NULL){// 找到要删除的节点并进行删除if (p->data == deldata){q->next = p->next; // 将q指向的节点指向被删除节点的下一个节点p->next = NULL; // p节点的next指针指向NULLfree(p); // 释放p后此时p是野指针p = q->next;// 将p指向被删除节点的下一个节点}else{p = p->next;q = q->next;} }// 遍历到最后一个节点if (p->next == NULL){// 若最后一个节点是要删除的节点,则删除if (p->data == deldata){q->next = p->next;p->next = NULL;free(p);}else{printf("最后一个节点不是要删除的节点\n");return 0;}}
}
// 修改节点
int list_update_node(int old_data, int new_data, single_list_t *list)
{single_list_t *p = list;while (p->next != NULL){p = p->next; // p往后移动if (p->data == old_data){p->data = new_data;}}return 0;
}// 4.遍历链表,打印节点数据
int list_show(single_list_t *list)
{single_list_t *p = list;while (p->next != NULL){p = p->next;printf("当前p指向的节点数据:%d\n", p->data);}
}int main(int argc, char const *argv[])
{// 初始化单链表single_list_t *my_list = single_list_init();// 往链表插入数据insert_list(15, my_list);insert_list(18, my_list);insert_list(15, my_list);insert_list(25, my_list);insert_list(666, my_list);insert_list(123, my_list);insert_list(11111111, my_list);insert_list_mid(666, 777, my_list);insert_list_mid(1, 222, my_list);insert_list_head(15, my_list);printf("============插入的节点============\n");list_show(my_list);printf("============插入的节点============\n");// 删除节点list_delnode(25,my_list);printf("============删除后的节点============\n");list_show(my_list); // 打印数据printf("============删除后的节点============\n");// 修改数据list_update_node(123, 234, my_list);printf("============修改后的节点============\n");list_show(my_list); // 打印数据printf("============修改后的节点============\n");return 0;
}
/*
执行结果:
要插入的数据不存在
============插入的节点============
当前p指向的节点数据:15
当前p指向的节点数据:15
当前p指向的节点数据:18
当前p指向的节点数据:15
当前p指向的节点数据:25
当前p指向的节点数据:666
当前p指向的节点数据:777
当前p指向的节点数据:123
当前p指向的节点数据:11111111
============插入的节点============
最后一个节点不是要删除的节点
============删除后的节点============
当前p指向的节点数据:15
当前p指向的节点数据:15
当前p指向的节点数据:18
当前p指向的节点数据:15
当前p指向的节点数据:666
当前p指向的节点数据:777
当前p指向的节点数据:123
当前p指向的节点数据:11111111
============删除后的节点============
============修改后的节点============
当前p指向的节点数据:15
当前p指向的节点数据:15
当前p指向的节点数据:18
当前p指向的节点数据:15
当前p指向的节点数据:666
当前p指向的节点数据:777
当前p指向的节点数据:234
当前p指向的节点数据:11111111
============修改后的节点============
*/
相关文章:

【数据结构】单链表的使用
单链表的使用 1、基本概念2、链表的分类3、链表的基本操作a、单链表节点设计b、单链表初始化c、单链表增删节点**节点头插:****节点尾插:****新节点插入指定节点后:**节点删除: d、单链表修改节点e、单链表遍历,并打印…...
外键约束的应用层维护
1.前言 一般来说 对于不同表格之间的属性约束 我们通常直接使用数据库已经实现好的外键来完成 但是数据库底层实现的外键他的性能很差 这是因为在执行数据库修改操作时 他需要遍历其他所有的表来找出其中可能相关联的属性 一并进行数据库修改(应用层的维护则只需要遍历所有关联…...

springboot整合log4j2日志框架1
目录 一 log4j基本知识 1.1 log4j的日志级别 1.2 log4j的日志文件结构* 1.2.1 概述 1.2.2 详解 1.3 log4j的日志格式化api 1.3.1 api详解 1.3.2 演示案例 1.3.3 演示案例 1.4 log4j中onmatch和onmismatch的区别* 1.4.1 案例 1.4.2 onmatch的api 1.5 logback&#x…...
06 - Django 视图view
HttpRequest 和 HttpResponse Django中的视图主要用来接受Web请求,并做出响应。 视图的本质就是一个Python中的函数 视图的响应分为两大类 以Json数据形式返回(JsonResponse)以网页的形式返回 重定向到另一个网页 (HttpResponseRedirect)错误视图(4XX,5XX) (Htt…...
基于云计算的资源管理系统
基于云计算的资源管理系统是一种将云计算技术与资源管理技术相结合,以实现资源高效利用和管理的系统。以下是对该系统的详细分析: 一、系统概述 云计算是一种基于网络的计算模式,通过将计算资源和数据存储在云端服务器上,使用户…...
从0入门自主空中机器人-3-【环境与常用软件安装】
关于本课程: 本次课程是一套面向对自主空中机器人感兴趣的学生、爱好者、相关从业人员的免费课程,包含了从硬件组装、机载电脑环境设置、代码部署、实机实验等全套详细流程,带你从0开始,组装属于自己的自主无人机,并让…...
electron node-api addon开发
解决方案入口 拷贝日志以及json等第三方源码 增加包含目录 编写接口 默认模板已经有一个回调函数了 照葫芦画瓢就行 其中几个重要的点要注意 1.参数传入 比如如下的例子: 头文件定义: public:下增加 Napi::Value StartAnswer (const Napi::Callb…...
如何在嵌入式系统或计算机系统中验证boot程序
在嵌入式系统或计算机系统中,验证boot程序(引导程序)的正确性至关重要,因为它负责初始化系统硬件、加载操作系统内核,并设置系统环境。以下是一些常用的验证boot程序的方法: 一、硬件验证 示波器与逻辑分…...
scala基础学习_运算符
文章目录 scala运算符算术运算符关系运算符逻辑运算符位运算符其他运算符赋值运算符 scala运算符 在 Scala 中,运算符通常被定义为方法。这意味着你可以将运算符视为对象上的方法调用。以下是一些常用的运算符及其对应的操作: 算术运算符 :…...

【ANGULAR网站开发】初始环境搭建
1. 初始化angular项目 1.1 创建angular项目 需要安装npm和nodejs,这边不在重新安装 直接安装最新版本的angular npm install -g angular/cli安装指定大版本的angular npm install -g angular/cli181.2 启动angular 使用idea启动 控制台启动 ng serve启动成功…...

【Java】面试题 并发安全 (2)
文章目录 可重入锁(ReentrantLock)知识总结1. 可重入锁概念与特点2. 基本语法与使用注意事项3. 底层实现原理4. 面试回答要点 synchronized与lock的区别死锁相关面试题讲解死锁产生的四个条件ConcurrentHashMap2. JDK1.7的ConcurrentHashMap结构添加数据…...

springboot启动不了 因一个spring-boot-starter-web底下的tomcat-embed-core依赖丢失
这个包丢失了 启动不了 起因是pom中加入了 <tomcat.version></tomcat.version>版本指定,然后idea自动编译后,包丢了,删除这个配置后再也找不回来, 这个包正常在 <dependency><groupId>org.springframe…...

React 组件的通信方式
在 React 应用开发中,组件之间的通信是构建复杂用户界面和交互逻辑的关键。正确地实现组件通信能够让我们的应用更加灵活和易于维护。以下是几种常见的 React组件通信方式。 一、父子组件通信 1. 通过 props 传递数据(父组件向子组件传递数据࿰…...

WAV文件双轨PCM格式详细说明及C语言解析示例
WAV文件双轨PCM格式详细说明及C语言解析示例 一、WAV文件双轨PCM格式详细说明1. WAV文件基本结构2. PCM编码方式3. 双轨PCM格式详细说明二、C语言解析WAV文件的代码示例代码说明一、WAV文件双轨PCM格式详细说明 WAV文件是一种用于存储未压缩音频数据的文件格式,广泛应用于音频…...
【ES6复习笔记】数值扩展(16)
介绍 在 JavaScript 中,数值扩展提供了一些额外的功能,使得处理数值变得更加方便。本教程将介绍一些常用的数值扩展方法和属性。 1. Number.EPSILON Number.EPSILON 是 JavaScript 表示的最小精度。它的值接近于 2.2204460492503130808472633361816E-…...

百度热力图数据日期如何选择
目录 1、看日历2、看天气 根据研究内容定,一般如果研究城市活力的话,通常会写“非重大节假日,非重大活动,非极端天气等”。南方晴天不多,有小雨或者中雨都可认为没有影响,要不然在南方很难找到完全一周没有…...
Vue.js 高级组件开发:设计模式与实践
Vue.js 高级组件开发:设计模式与实践 引言一、组合式 API 与动态依赖注入1. 基于 provide/inject 的动态依赖2. 动态依赖注入与懒加载 二、动态渲染与自定义渲染函数1. 使用 Render 函数动态生成内容2. 自定义 vnode 操作 三、复杂场景下的动态表单生成与验证四、高…...

《一文读懂卷积网络CNN:原理、模型与应用全解析》
《一文读懂卷积网络CNN:原理、模型与应用全解析》 一、CNN 基本原理大揭秘(一)从人类视觉到 CNN 灵感(二)核心组件详解 二、经典 CNN 模型巡礼(一)LeNet-5:开山鼻祖(二&a…...

MONI后台管理系统-数据敏感字段存储加密
前言: 在我们数据库中,存在很多的敏感数据,如用户表中,存在用户电话、身份证号、邮箱等属于用户的敏感信息,我们通常在存入数据库后,将其进行加密存储,以此来保证数据安全性。 …...
熟悉各类游戏设计模式的用途与限制,如 factory、strategy、mvc、object pool 等
良好的系统分析与设计能力要求开发者熟悉并正确运用各种设计模式来解决特定问题。设计模式是一种针对特定问题的通用解决方案,可提高代码的可复用性、可维护性和可扩展性。以下是对一些常见游戏设计模式的详细分析,包括其用途、限制和代码示例。 一、工厂…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...

遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...

LabVIEW双光子成像系统技术
双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制,展现出显著的技术优势: 深层组织穿透能力:适用于活体组织深度成像 高分辨率观测性能:满足微观结构的精细研究需求 低光毒性特点:减少对样本的损伤…...
go 里面的指针
指针 在 Go 中,指针(pointer)是一个变量的内存地址,就像 C 语言那样: a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10,通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...