数据结构----链表头插中插尾插
一、链表的基本概念
链表是一种线性数据结构,它由一系列节点组成。每个节点包含两个主要部分:
- 数据域:用于存储数据元素,可以是任何类型的数据,如整数、字符、结构体等。
- 指针域:用于存储下一个节点(在单链表中)或前一个节点与下一个节点(在双链表中)的地址。
与数组不同,链表中的节点在内存中不是连续存储的。它们通过指针链接在一起,形成一个链状结构。这种非连续存储方式使得链表在插入和删除操作上具有一定的优势。
二、单链表
- 结构
- 单链表节点的定义(以C语言为例):
typedef struct ListNode {int data; // 数据域,可以根据需要存储不同类型的数据struct ListNode *next; // 指针域,指向下一个节点
} ListNode;
- 这里,
data用于存储数据,next是一个指向同类型结构体的指针,用于指向下一个节点。
- 基本操作
- 创建节点
- 函数实现:
- 创建节点
ListNode* createNode(int value) {ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));if (newNode == NULL) {// 内存分配失败处理return NULL;}newNode->data = value;newNode->next = NULL;return newNode;
}
- 解释:- 首先使用`malloc`函数分配足够的内存来存储一个`ListNode`结构体。如果内存分配成功,将传入的值赋给新节点的数据域,并将指针域初始化为`NULL`,表示这是一个孤立的节点,目前没有下一个节点。最后返回新创建的节点。
- 插入节点
- 头插法
- 函数实现:
- 头插法
ListNode* insertAtHead(ListNode* head, int value) {ListNode* newNode = createNode(value);newNode->next = head;return newNode;
}
- 解释:- 首先调用`createNode`函数创建一个新节点。然后将新节点的`next`指针指向当前的头节点(如果`head`为`NULL`,则新节点成为唯一的节点)。最后,将新节点作为新的头节点返回。这意味着每次插入操作都会使新节点成为链表的第一个节点。- **尾插法**- 函数实现:
ListNode* insertAtTail(ListNode* head, int value) {ListNode* newNode = createNode(value);if (head == NULL) {return newNode;}ListNode* current = head;while (current->next!= NULL) {current = current->next;}current->next = newNode;return head;
}
- 解释:- 同样先创建新节点。如果链表为空(`head`为`NULL`),则直接返回新节点作为头节点。如果链表不为空,通过一个循环找到链表的最后一个节点(即当前节点的`next`指针为`NULL`的节点)。找到后,将最后一个节点的`next`指针指向新节点,从而将新节点插入到链表的尾部。最后返回原头节点,因为尾插法不会改变头节点。- **中插法(在指定位置插入)**- 函数实现:
ListNode* insertAtPosition(ListNode* head, int value, int position) {if (position == 0) {return insertAtHead(head, value);}ListNode* newNode = createNode(value);ListNode* current = head;for (int i = 0; i < position - 1 && current!= NULL; i++) {current = current->next;}if (current == NULL) {return head;}newNode->next = current->next;current->next = newNode;return head;
}
- 解释:- 首先判断要插入的位置是否为0,如果是,则调用头插法函数进行插入。如果不是0,先创建新节点。然后通过一个循环找到要插入位置的前一个节点(循环条件确保不会超出链表长度且能找到正确位置)。如果在遍历过程中发现当前节点为`NULL`,说明已经到达链表末尾但还没找到插入位置,此时直接返回原链表。找到插入位置的前一个节点后,将新节点的`next`指针指向当前节点的下一个节点,再将当前节点的`next`指针指向新节点,完成插入操作。最后返回原头节点。
- 删除节点
- 函数实现:
ListNode* deleteNode(ListNode* head, int target) {if (head == NULL) {return NULL;}if (head->data == target) {ListNode* temp = head;head = head->next;free(temp);return head;}ListNode* current = head;while (current->next!= NULL && current->next->data!= target) {current = current->next;}if (current->next!= NULL) {ListNode* temp = current->next;current->next = current->next->next;free(temp);}return head;
}
- 解释:- 首先判断链表是否为空,如果为空则无法删除,直接返回`NULL`。如果头节点的数据就是要删除的数据,那么将头节点的下一个节点作为新的头节点,并释放原来的头节点,然后返回新头节点。如果头节点不是要删除的节点,通过循环找到要删除节点的前一个节点(通过判断当前节点的下一个节点的数据是否为目标数据)。找到后,将前一个节点的`next`指针指向要删除节点的下一个节点,然后释放要删除的节点,最后返回原头节点。
- 遍历链表
- 函数实现:
void traverseList(ListNode* head) {ListNode* current = head;while (current!= NULL) {printf("%d ", current->data);current = current->next;}printf("\n");
}
- 解释:- 从链表的头节点开始,通过循环依次访问每个节点的数据域,并将其打印出来。每次循环将当前节点更新为下一个节点,直到当前节点为`NULL`,表示已经遍历完整个链表。

三、双链表
- 结构
- 双链表节点的定义(以C语言为例):
typedef struct DoubleListNode {int data;struct DoubleListNode *prev;struct DoubleListNode *next;
} DoubleListNode;
- 这里,
data用于存储数据,prev是指向前一个节点的指针,next是指向后一个节点的指针。
- 基本操作
- 创建节点
- 函数实现:
- 创建节点
DoubleListNode* createDoubleNode(int value) {DoubleListNode* newNode = (DoubleListNode*)malloc(sizeof(DoubleListNode));if (newNode == NULL) {return NULL;}newNode->data = value;newNode->prev = NULL;newNode->next = NULL;return newNode;
}
- 解释:- 与单链表创建节点类似,使用`malloc`分配内存。成功分配后,将数据赋给新节点的数据域,并将`prev`和`next`指针都初始化为`NULL`,因为新创建的节点暂时没有前后连接的节点。最后返回新节点。
- 插入节点
- 例如,在双链表头部插入节点:
- 函数实现:
- 例如,在双链表头部插入节点:
DoubleListNode* insertAtHeadDouble(DoubleListNode* head, int value) {DoubleListNode* newNode = createDoubleNode(value);if (head == NULL) {return newNode;}newNode->next = head;head->prev = newNode;return newNode;
}
- 解释:- 先创建新节点。如果链表为空,直接返回新节点作为头节点。如果链表不为空,将新节点的`next`指针指向当前头节点,同时将当前头节点的`prev`指针指向新节点,最后将新节点作为新的头节点返回。
- 删除节点
- 例如,删除双链表中值为
target的节点:- 函数实现:
- 例如,删除双链表中值为
DoubleListNode* deleteDoubleNode(DoubleListNode* head, int target) {if (head == NULL) {return NULL;}if (head->data == target) {DoubleListNode* temp = head;if (head->next!= NULL) {head->next->prev = NULL;}head = head->next;free(temp);return head;}DoubleListNode* current = head;while (current->next!= NULL && current->next->data!= target) {current = current->next;}if (current->next!= NULL) {DoubleListNode* temp = current->next;if (temp->next!= NULL) {temp->next->prev = current;}current->next = temp->next;free(temp);}return head;
}
- 解释:- 首先判断链表是否为空,为空则无法删除,返回`NULL`。如果头节点的数据是要删除的数据,判断头节点的下一个节点是否存在,如果存在则将其`prev`指针置为`NULL`,然后将头节点更新为下一个节点,并释放原来的头节点,最后返回新头节点。如果头节点不是要删除的节点,通过循环找到要删除节点的前一个节点(通过判断当前节点的下一个节点的数据是否为目标数据)。找到后,如果要删除的节点的下一个节点存在,则将其下一个节点的`prev`指针指向当前节点,然后将当前节点的`next`指针指向要删除节点的下一个节点,最后释放要删除的节点,返回原头节点。
- 遍历链表
- 例如,从头部开始向前遍历:
- 函数实现:
- 例如,从头部开始向前遍历:
void traverseDoubleListForward(DoubleListNode* head) {DoubleListNode* current = head;while (current!= NULL) {printf("%d ", current->data);current = current->next;}printf("\n");
}
- 解释:- 与单链表的遍历类似,从头节点开始,通过循环依次访问每个节点的数据域并打印,每次将当前节点更新为下一个节点,直到当前节点为`NULL`。双链表还可以从尾部开始向后遍历,原理类似,只是使用`prev`指针。
四、循环链表
-
结构
- 循环链表分为循环单链表和循环双链表。
- 循环单链表是最后一个节点的
next指针指向头节点,形成一个环。 - 循环双链表是最后一个节点的
next指针指向头节点,头节点的prev指针指向最后一个节点,形成一个双向的环。
-
基本操作
- 创建循环单链表
- 例如,通过尾插法创建循环单链表:
- 函数实现:
- 例如,通过尾插法创建循环单链表:
- 创建循环单链表
ListNode* createCircularList(int values[], int size) {if (size == 0) {return NULL;}ListNode* head = createNode(values[0]);ListNode* current = head;for (int i = 1; i < size; i++) {ListNode* newNode = createNode(values[i]);current->next = newNode;current = newNode;}current->next = head;return head;
}
- 解释:- 首先判断数组大小是否为0,如果是则返回`NULL`。如果不为0,先创建头节点,然后通过循环依次创建新节点并将其插入到链表尾部。最后将最后一个节点的`next`指针指向头节点,形成循环链表,并返回头节点。
- 遍历循环单链表
- 例如:
- 函数实现:
- 例如:
void traverseCircularList(ListNode* head) {if (head == NULL) {return;}ListNode* current = head;do {printf("%d ", current->data);current = current->next;} while (current!= head);printf("\n");
}
- 解释:- 首先判断链表是否为空,如果为空则直接返回。如果不为空,从头节点开始遍历,通过`do - while`循环依次访问每个节点的数据域并打印。循环条件是当前节点不等于头节点,因为是循环链表,当再次回到头节点时表示遍历结束。最后换行。
循环链表在一些特定的应用场景中非常有用,例如操作系统中的资源分配循环队列等。双链表在需要双向访问数据的场景下有优势,而单链表则在空间利用和简单操作场景下较为常用。
相关文章:
数据结构----链表头插中插尾插
一、链表的基本概念 链表是一种线性数据结构,它由一系列节点组成。每个节点包含两个主要部分: 数据域:用于存储数据元素,可以是任何类型的数据,如整数、字符、结构体等。指针域:用于存储下一个节点&#…...
设计模式-读书笔记
确认好: 模式名称 问题:在何时使用模式,包含设计中存在的问题以及问题存在的原因 解决方案:设计模式的组成部分,以及这些组成部分之间的相互关系,各自的职责和协作方式,用uml类图和核心代码描…...
c语言----选择结构
基本概念 选择结构是C语言中用于根据条件判断来执行不同代码块的结构。它允许程序在不同的条件下执行不同的操作,使程序具有决策能力。 if语句 单分支if语句 语法格式: if (条件表达式) { 执行语句块; } 功能: 当条件表达式的值为真&#…...
KS曲线python实现
目录 实战 实战 # 导入第三方模块 import pandas as pd import numpy as np import matplotlib.pyplot as plt# 自定义绘制ks曲线的函数 def plot_ks(y_test, y_score, positive_flag):# 对y_test重新设置索引y_test.index np.arange(len(y_test))# 构建目标数据集target_dat…...
解决matplotlib中文乱码问题
进入python,查看缓存 import matplotlib as mpl print(mpl.get_cachedir())如果结果为/Users/xxx/.matplotlib 那么就rm -rf /Users/xxx/.matplotlib 然后 mkdir ~/.fonts cd ~/.fonts wget http://129.204.205.246/downloads/SimHei.ttfsudo apt-get install fo…...
实操给桌面机器人加上超拟人音色
前面我们讲了怎么用CSK6大模型开发板做一个桌面机器人充当AI语音助理,近期上线超拟人方案,不仅大模型语音最快可以1秒内回复,还可以让我们的桌面机器人使用超拟人音色、具备声纹识别等能力,本文以csk6大模型开发板为例实操怎么把超…...
git stash 的文件如何找回
在Git中,如果你使用了git stash命令来保存你的工作进度,但之后想要找回这些被stash的文件,你可以按照以下步骤进行操作: 1. 查看stash列表 首先,使用git stash list命令来查看当前保存的所有stash记录。这个命令会列出…...
皮肤伤口分割数据集labelme格式248张5类别
数据集格式:labelme格式(不包含mask文件,仅仅包含jpg图片和对应的json文件) 图片数量(jpg文件个数):284 标注数量(json文件个数):284 标注类别数:5 标注类别名称:["bruises","burns","cu…...
uni-app开发AI康复锻炼小程序,帮助肢体受伤患者康复!
**提要:**近段时间我们收到多个康复机构用户,咨询AI运动识别插件是否可以应用于肢力运动受限患者的康复锻炼中来,插件是可以应用到AI康复锻炼中的,今天小编就为您介绍一下AI运动识别插件在康腹锻炼中的应用场景。 一、康复机构的应…...
双内核架构 Xenomai 4 安装教程
Xenomai 4是一种双内核架构, 继承了Xenomai系列的特点,通过在Linux内核中嵌入一个辅助核心(companion core),来提供实时能力。这个辅助核心专门处理那些需要极低且有界响应时间的任务。 本文将在官网教程(https://evlproject.org/…...
【redis的使用、账号流程、游戏服Handler的反射调用】1.自增id 2.全局用户名这样子名字唯一 3.
一、web服 1)账号注册 // 用于唯一命名服务 com.xinyue.game.center.business.account.logic.AccountRegisterService#accountRegister public void accountRegister(AccountEntity account) {accountManager.checkUsername(account.getUsername());accountManager.checkPass…...
neo4j 图表数据导入到 TuGraph
neo4j 图表数据导入到 TuGraph 代码文件说明后文 前言:近期在引入阿里的 TuGraph 图数据库,需要将 原 neo4j 数据导入到新的 tugraph 数据库中。预期走csv文件导入导出,但因为格式和数据库设计问题,操作起来比较麻烦(可能是个人没…...
启动报错java.lang.NoClassDefFoundError: ch/qos/logback/core/status/WarnStatus
报错信息图片 日志: Exception in thread "Quartz Scheduler [scheduler]" java.lang.NoClassDefFoundError: ch/qos/logback/core/status/WarnStatus先说我自己遇到的问题,我们项目在web设置了自定义的log输出路径,多了一个 / 去…...
【ubuntu18.04】ubuntu18.04挂在硬盘出现 Wrong diagnostic page; asked for 1 got 8解决方案
错误日志 [ 8754.700227] usb 2-3: new full-speed USB device number 3 using xhci_hcd [ 8754.867389] usb 2-3: New USB device found, idVendor0e0f, idProduct0002, bcdDevice 1.00 [ 8754.867421] usb 2-3: New USB device strings: Mfr1, Product2, SerialNumber0 [ 87…...
kubeadm安装K8s高可用集群之集群初始化及master/node节点加入calico网络插件安装
系列文章目录 1.kubeadm安装K8s高可用集群之基础环境配置 2.kubeadm安装K8s集群之高可用组件keepalivednginx及kubeadm部署 3.kubeadm安装K8s高可用集群之集群初始化及master/node节点加入集群calico网络插件安装 kubeadm安装K8s高可用集群之集群初始化及master/node节点加入ca…...
游戏何如防抓包
游戏抓包是指在游戏中,通过抓包工具捕获和分析游戏客户端与服务器之间传输的封包数据的过程。抓包工具可实现拦截、篡改、重发、丢弃游戏的上下行数据包,市面上常见的抓包工具有WPE、Fiddler和Charles Proxy等。 抓包工具有两种实现方式,一类…...
【LeetCode】每日一题 2024_12_19 找到稳定山的下标(模拟)
前言 每天和你一起刷 LeetCode 每日一题~ 最近力扣的每日一题出的比较烂,难度过山车,导致近期的更新都三天打鱼,两天断更了 . . . LeetCode 启动! 题目:找到稳定山的下标 代码与解题思路 先读题:最重要…...
运维 mysql、redis 、RocketMQ性能排查
MySQL查看数据库连接数 1. SHOW STATUS命令-查询当前的连接数 MySQL 提供了一个 SHOW STATUS 命令,可以用来查看服务器的状态信息,包括当前的连接数。 SHOW STATUS LIKE Threads_connected;这个命令会返回当前连接到服务器的线程数,即当前…...
[SAP ABAP] 将内表数据转换为HTML格式
从sflight数据库表中检索航班信息,并将这些信息转换成HTML格式,然后下载或显示在前端 开发步骤 ① 自定义一个数据类型 ty_sflight 来存储航班信息 ② 声明内表和工作区变量,用于存储表头、字段、HTML内容和航班详细信息以及创建字段目录lt…...
LLM大语言模型私有化部署-使用Dify与Qwen2.5打造专属知识库
背景 Dify 是一款开源的大语言模型(LLM) 应用开发平台。其直观的界面结合了 AI 工作流、 RAG 管道、 Agent 、模型管理、可观测性功能等,让您可以快速从原型到生产。相比 LangChain 这类有着锤子、钉子的工具箱开发库, Dify 提供了更接近生产需要的完整…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...
【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...
