数据结构-链表【chapter1】【c语言版】
目录
1 链表的优势:
2 链表的组成:
3.一般使用结构体的形式来实现链表:
4.单向链表实现(创建,遍历,释放):
4.1代码关键点备注:
5.查找节点:
5.1.按值查找节点
5.2.按位置查找节点
5.3 查找是否存在某个值
5.4. 查找链表中最后一个节点
5.5 查找链表中倒数第 k 个节点
6.删除节点
6.1 删除头节点
6.2删除尾节点
6.3.删除指定位置的节点
6.4.删除指定值的节点
6.5.释放整个链表
1 链表的优势:
- 动态大小:链表的大小可以根据需要动态调整,而数组在声明时大小固定。
- 插入和删除操作高效:在链表中,插入和删除操作不需要移动元素,只需修改指针即可,效率更高。
- 内存利用率高:链表可以根据需要分配内存,不会浪费空间。
2 链表的组成:
-
节点结构:链表由多个节点组成,每个节点通常包含两部分:
- 数据域:存储实际的数据。
- 指针域:指向下一个节点的指针(在双向链表中,还会有指向前一个节点的指针),第一个节点的指针域保存第二个节点的地址。
-
头指针:链表通常有一个头指针,指向链表的第一个节点。如果链表为空,头指针为NULL。
-
尾指针(可选):在某些实现中,链表还可能包含一个指向最后一个节点的指针,以便于在尾部插入节点时提高效率
3.一般使用结构体的形式来实现链表:
struct Node {int data; // 数据域struct Node* next; // 指针域,指向下一个节点
};
在正式手搓单向链表前,先复习一下二级指针:
指针:一个变量,它存储另一个变量的地址。例如,Node* head 是一个指向 Node 类型的指针。
二级指针:一个指向指针的指针。比如,Node** head 表示这是一个指向 Node* 的指针,通常用于传递指针的地址,以便在函数内可以修改这个指针的值。在链表的实现中,使用二级指针来传递指向头指针的地址,这样就可以在函数内部修改头指针的值。
4.单向链表实现(创建,遍历,释放):
下面是一个代码的示例:
#include <stdio.h>
#include <stdlib.h>// 定义链表节点结构体
struct Node {int data; // 节点数据struct Node* next; // 指向下一个节点的指针
};// 创建链表节点并添加到链表末尾
void createNode(struct Node** head, int value) {struct Node* newNode = (struct Node*)malloc(sizeof(struct Node)); // 分配新节点内存newNode->data = value; // 设置节点的数据newNode->next = NULL; // 新节点的下一个指针初始化为 NULL// 检查链表是否为空if (*head == NULL) {*head = newNode; // 如果链表为空,则将新节点设置为头节点} else {struct Node* temp = *head; // 使用 *head 访问当前头节点while (temp->next != NULL) { // 遍历链表到最后一个节点temp = temp->next;}temp->next = newNode; // 将新节点链接到链表末尾}
}// 打印链表中的所有节点
void printList(struct Node* head) {struct Node* temp = head; // struct Node* temp = head; 这一行定义了一个
//新的指针变量 temp,并将 head 的值(即头节点的地址)赋给它。while (temp != NULL) {printf("%d -> ", temp->data); // 打印当前节点的数据temp = temp->next; // 移动到下一个节点}printf("NULL\n"); // 链表结束标志
}// 释放链表内存
void freeList(struct Node** head) {struct Node* temp;while (*head != NULL) {temp = *head; // 备份当前头节点*head = (*head)->next; // 移动头指针到下一个节点free(temp); // 释放备份的节点内存}
}int main() {struct Node* head = NULL; // 初始化链表头节点为空int n, value;// 输入节点的个数printf("请输入链表节点的个数: ");scanf("%d", &n);// 使用 for 循环创建链表for (int i = 0; i < n; i++) {printf("请输入第 %d 个节点的值: ", i + 1);scanf("%d", &value);createNode(&head, value); // 通过二级指针传入头指针}// 打印链表内容printf("链表内容: ");printList(head);// 释放链表内存freeList(&head);return 0;
}
4.1代码关键点备注:
在void createNode中:
head是一个指向链表 头节点 的 指针的地址。(&head)- 通过传入二级指针,
createNode函数可以在链表为空时直接修改头指针的值,让新节点成为链表的头节点(即*head = newNode;的情况)。
在void printList(struct Node* head)中:
head是指向链表 头节点 的 指针,用于从链表的第一个节点开始进行遍历。struct Node* temp = head;这一行定义了一个新的指针变量temp,并将head的值(即头节点的地址)赋给它。(head)printList函数并不修改链表的结构或内容,只是逐一访问每个节点的数据并打印。因此,只需传入一个指向头节点的普通指针,而不需要使用二级指针(不需要修改头指针的值)。
5.查找节点:
5.1.按值查找节点
- 在链表中查找第一个数据与指定值匹配的节点,并返回该节点的指针。
- 如果找不到该值,通常返回
NULL。
struct Node* searchByValue(struct Node* head, int value) {struct Node* temp = head;while (temp != NULL) {if (temp->data == value) {return temp; // 找到匹配值的节点,返回指针}temp = temp->next; // 移动到下一个节点}return NULL; // 没有找到
}
5.2.按位置查找节点
- 按照链表中的位置(索引)查找节点。位置通常从
0开始,即第0个节点是头节点。 - 如果位置超出链表长度,可以返回
NULL。
struct Node* searchByPosition(struct Node* head, int position) {struct Node* temp = head;int currentIndex = 0;while (temp != NULL) {if (currentIndex == position) {return temp; // 找到指定位置的节点}temp = temp->next;currentIndex++;}return NULL; // 位置超出链表长度
}
5.3 查找是否存在某个值
- 在链表中查找是否存在某个值,只返回
1(找到)或0(未找到),而不返回节点。
int containsValue(struct Node* head, int value) {struct Node* temp = head;while (temp != NULL) {if (temp->data == value) {return 1; // 找到该值,返回 1}temp = temp->next;}return 0; // 没找到该值,返回 0
}
5.4. 查找链表中最后一个节点
- 可以用来获取链表的尾节点。
struct Node* getLastNode(struct Node* head) {if (head == NULL) return NULL; // 链表为空struct Node* temp = head;while (temp->next != NULL) {temp = temp->next;}return temp; // 返回最后一个节点
}
5.5 查找链表中倒数第 k 个节点
- 经典问题,常用 双指针 方法实现。用两个指针
fast和slow,它们都从链表的头节点head出发,但fast会比slow先走k步。然后,让两个指针同时移动,直到fast到达链表的末尾。此时,slow就刚好停在倒数第k个节点的位置。 -
struct Node* getKthFromEnd(struct Node* head, int k) {struct Node *fast = head, *slow = head;// 让 fast 指针先走 k 步for (int i = 0; i < k; i++) {if (fast == NULL) return NULL; // 链表长度小于 kfast = fast->next;}// 同时移动 fast 和 slow 指针,直到 fast 到达链表末尾while (fast != NULL) {fast = fast->next;slow = slow->next;}return slow; // slow 指针就是倒数第 k 个节点 }6.删除节点
6.1 删除头节点
void deleteHead(struct Node** head) {if (*head == NULL) return; // 链表为空,无法删除struct Node* temp = *head; // 备份头节点*head = (*head)->next; // 更新头节点为下一个节点free(temp); // 释放备份的头节点内存
}
6.2删除尾节点
删除链表的最后一个节点。需要遍历链表,找到倒数第二个节点并将其 next 指针设为 NULL。
void deleteTail(struct Node** head) {if (*head == NULL) return; // 链表为空,无法删除if ((*head)->next == NULL) { // 如果只有一个节点free(*head);*head = NULL; // 更新头指针为 NULLreturn;}struct Node* temp = *head;while (temp->next->next != NULL) { // 遍历到倒数第二个节点temp = temp->next;}free(temp->next); // 释放最后一个节点temp->next = NULL; // 将倒数第二个节点的 next 设为 NULL
}
1 -> 2 -> 3 -> NULL
在执行删除尾节点的操作时:
temp会遍历到节点2(倒数第二个节点),temp->next会指向节点3(最后一个节点)。- 当我们执行
free(temp->next);,节点3被释放。 - 然后执行
temp->next = NULL;,使得节点2变成链表的新尾节点,链表现在变为:
1 -> 2 -> NULL
6.3.删除指定位置的节点
根据给定的位置删除节点。
void deleteAtPosition(struct Node** head, int position) {if (*head == NULL) return; // 链表为空,无法删除if (position == 0) { // 如果要删除的是头节点deleteHead(head);return;}struct Node* temp = *head;for (int i = 0; i < position - 1; i++) {if (temp == NULL || temp->next == NULL) {printf("位置超出链表长度\n");return; // 位置超出链表长度,无法删除}temp = temp->next; // 遍历到要删除节点的前一个节点}struct Node* nodeToDelete = temp->next; // 要删除的节点if (nodeToDelete == NULL) return; // 如果没有下一个节点,无法删除temp->next = nodeToDelete->next; // 将前一个节点的 next 指向要删除节点的下一个节点free(nodeToDelete); // 释放要删除的节点内存
}
6.4.删除指定值的节点
删除链表中第一个值匹配指定值的节点
void deleteByValue(struct Node** head, int value) {if (*head == NULL) return; // 链表为空,无法删除struct Node* temp = *head;// 如果头节点的值就是要删除的值if (temp->data == value) {deleteHead(head);return;}// 遍历链表查找匹配的节点while (temp->next != NULL) {if (temp->next->data == value) { // 找到匹配的节点struct Node* nodeToDelete = temp->next; // 备份要删除的节点temp->next = nodeToDelete->next; // 链接前一个节点与后一个节点free(nodeToDelete); // 释放要删除的节点内存return; // 删除完毕,退出函数}temp = temp->next; // 移动到下一个节点}
}
6.5.释放整个链表
如果需要删除整个链表,释放所有节点内存。
void freeList(struct Node** head) {struct Node* temp;while (*head != NULL) {temp = *head; // 备份当前头节点*head = (*head)->next; // 移动头指针到下一个节点free(temp); // 释放备份的节点内存}
}
相关文章:
数据结构-链表【chapter1】【c语言版】
目录 1 链表的优势: 2 链表的组成: 3.一般使用结构体的形式来实现链表: 4.单向链表实现(创建,遍历,释放): 4.1代码关键点备注: 5.查找节点: 5.1.按值查找节点 5.2.按位置查找节点 5.3 …...
OJ05:989. 数组形式的整数加法
目录 题目思路分析代码展示 题目 整数的 数组形式 num 是按照从左到右的顺序表示其数字的数组。 例如,对于 num 1321 ,数组形式是 [1,3,2,1] 。 给定 num ,整数的 数组形式 ,和整数 k ,返回 整数 num k 的 数组形…...
山东布谷科技:关于直播源码|语音源码|一对一直播源码提交App Store的流程及重构建议
自从YY、六间房开启国内聊天室和秀场等网红盛行的网络红利时代以来,紧随其后国内各大音视频平台相应出现,先有映客花椒等直播平台的风头正劲,后有功能板块更丰富的头条抖音Tiktok等,盈利功能点不仅仅有直播PK连麦等礼物打赏功能&a…...
docker搭建guacamole,web远程桌面
Apache Guacamole 是一个客户端无插件的远程桌面网关。它支持标准协议,如 VNC、RDP 和 SSH。您可以使用任何现代 web 浏览器连接到您的桌面环境,而无需安装额外的软件。使用 Docker Compose 部署 Guacamole,如果没有docker-compose请先执行su…...
.baxia勒索病毒来袭:数据恢复与防护措施详解
导言 在当今这个信息化高速发展的时代,数据已成为企业和个人的核心资产,其价值不可估量。然而,随着网络技术的不断进步,网络安全威胁也日益严峻,其中勒索病毒作为一种新型的网络攻击手段,尤其是.baxia勒索…...
[UUCTF 2022 新生赛]ezpop 详细题解(字符串逃逸)
知识点: php反序列化字符串逃逸 php反序列化魔术方法 构造pop链 变量引用 其实这一题还是比较简单的,只要看懂代码,并且理解为什么要用反序列化字符串逃逸,下面会详细解释 题目源码: <?php //flag in flag.php error_reporting(0); class UUCTF{public $name,$key,$…...
【Zynq UltraScale+ RFSoC】DFE
DFE : digital front-end 数字前端 Xilinx Zynq RFSoC DFE 是一款突破性的灵活应变无线电平台,可强化数字前端 (DFE),用于 5G 大规模无线电部署和广泛的其他射频应用。 Zynq RFSoC DFE 基于唯一经过生产验证的自适应单芯片无线电…...
Ubuntu学习笔记 - Day3
文章目录 学习目标:学习内容:学习笔记:vim简介vim键盘图工作模式 vim移动光标操作上下左右移动翻页 vim替换和删除操作替换删除 vim插入模式详解进入模式搜索 vim底行模式操作保存退出行号 学习目标: 一周掌握 Linux基本使用技巧 …...
scala list系列
dd list:有序的,链表 1.建立 不可变列表 2.通过下标来访问:下标从0开始 3.不能修改 4.添加 5.删除 6.合并 7.查找,判断元素是否存在 8.遍历...
TLS协议基本原理与Wireshark分析_wireshark分析tls协议
01****背 景 随着车联网的迅猛发展,汽车已经不再是传统的机械交通工具,而是智能化、互联化的移动终端。然而,随之而来的是对车辆通信安全的日益严峻的威胁。在车联网生态系统中,车辆通过无线网络与其他车辆、基础设施以及云端服务…...
【359】基于springboot的智慧草莓基地管理系统
摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本智慧草莓基地管理系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据…...
【智能算法应用】遗传算法求解车间布局优化问题
摘要 本文研究了基于遗传算法(Genetic Algorithm, GA)的车间布局优化方法。遗传算法是一种基于自然选择和遗传机制的优化算法,通过编码布局方案、交叉和变异操作生成新的布局个体,选择最优的车间布局方案。实验结果表明ÿ…...
java 中List 的使用
List集合是Collection接口的子接口,其下有两个实现类分别为ArrayList和 LinkedList List是一个接口,不能用new创建对象,需要用 ArrayList类 和 LinkedList类 来创建 特点 有序:存储元素的顺序和取出元素的顺序一致可以重复&…...
CSS学习之Grid网格布局基本概念、容器属性
网格布局 网格布局(Grid)是将网页划分成一个个网格单元,可任意组合不同的网格,轻松实现各种布局效果,也是目前CSS中最强大布局方案,比Flex更强大。 基本概念 容器和项目 当一个 HTML 元素将 display 属性…...
前后端交互接口(二)
前后端交互接口(二) 前言 在上一集我们约定了我们前后端交互接口的三条规则。这一集我们就先来看一看我们的一些proto文件。 浅看proto文件 在看文件之前,还是简单谈谈Protobuf Protobuf通过一个.proto文件定义数据结构,这个…...
HarmonyOs DevEco Studio小技巧28--部分鸿蒙生命周期详解
目录 前言 页面和自定义组件生命周期 页面生命周期 onPageShow --- 表示页面已经显示 onPageHide --- 表示页面已经隐藏 onBackPress --- 表示用户点击了返回键 组件生命周期 aboutToAppear --- 表示组件即将出现 onDidBuild --- 表示组件已经构建完成 aboutToDisap…...
STM32(hal库)的msp初始化HAL_TIM_Base_MspInit有什么用?为什么单独设置这个,而不是在timer_init()函数里直接初始化?
在STM32 HAL库中,HAL_TIM_Base_MspInit 函数是一个与定时器(TIM)相关的底层初始化函数,其名称中的 "Msp" 代表 MCU Service Package(微控制器服务包),这是HAL库的一部分,用…...
三品PLM系统如何规范企业图纸文档资料电子化管理
三品PLM系统如何规范企业图纸文档资料电子化管理 图纸文档是企业设计、生产、管理的重要信息载体,是产品设计与生产维护的关键。传统纸质归档已无法满足现代需求,电子化管理成为提升效率和文档一致性的重要手段。然而,许多企业在实施电子化管…...
鸿蒙开发:arkts 如何读取json数据
为了支持ArkTS语言的开发,华为提供了完善的工具链,包括代码编辑器、编译器、调试器、测试工具等。开发者可以使用这些工具进行ArkTS应用的开发、调试和测试。同时,华为还提供了DevEco Studio这一一站式的开发平台,为运行在Harmony…...
Java学习篇之JVM 调优
Java学习篇之JVM 调优 一、JVM 是什么?二、JVM 官方参数建议三、JVM调优的场景四、如何监控JVM五、JVM调优的流程步骤1. 明确优化目标2. 监控和分析3. 确定调优参数4. 实施调优策略5. 持续观察和调整6. 定期评估和优化 一、JVM 是什么? JVM,…...
手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...
C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...
