当前位置: 首页 > news >正文

数据结构-链表【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 链表的优势:

  1. 动态大小:链表的大小可以根据需要动态调整,而数组在声明时大小固定。
  2. 插入和删除操作高效:在链表中,插入和删除操作不需要移动元素,只需修改指针即可,效率更高。
  3. 内存利用率高:链表可以根据需要分配内存,不会浪费空间。

2 链表的组成:

  1. 节点结构:链表由多个节点组成,每个节点通常包含两部分:

    • 数据域:存储实际的数据。
    • 指针域:指向下一个节点的指针(在双向链表中,还会有指向前一个节点的指针),第一个节点的指针域保存第二个节点的地址。
  2. 头指针:链表通常有一个头指针,指向链表的第一个节点。如果链表为空,头指针为NULL。

  3. 尾指针(可选):在某些实现中,链表还可能包含一个指向最后一个节点的指针,以便于在尾部插入节点时提高效率

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 个节点

  • 经典问题,常用 双指针 方法实现。用两个指针 fastslow,它们都从链表的头节点 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 链表的优势&#xff1a; 2 链表的组成: 3.一般使用结构体的形式来实现链表&#xff1a; 4.单向链表实现(创建&#xff0c;遍历&#xff0c;释放)&#xff1a; 4.1代码关键点备注&#xff1a; 5.查找节点&#xff1a; 5.1.按值查找节点 5.2.按位置查找节点 5.3 …...

OJ05:989. 数组形式的整数加法

目录 题目思路分析代码展示 题目 整数的 数组形式 num 是按照从左到右的顺序表示其数字的数组。 例如&#xff0c;对于 num 1321 &#xff0c;数组形式是 [1,3,2,1] 。 给定 num &#xff0c;整数的 数组形式 &#xff0c;和整数 k &#xff0c;返回 整数 num k 的 数组形…...

山东布谷科技:关于直播源码|语音源码|一对一直播源码提交App Store的流程及重构建议

自从YY、六间房开启国内聊天室和秀场等网红盛行的网络红利时代以来&#xff0c;紧随其后国内各大音视频平台相应出现&#xff0c;先有映客花椒等直播平台的风头正劲&#xff0c;后有功能板块更丰富的头条抖音Tiktok等&#xff0c;盈利功能点不仅仅有直播PK连麦等礼物打赏功能&a…...

docker搭建guacamole,web远程桌面

Apache Guacamole 是一个客户端无插件的远程桌面网关。它支持标准协议&#xff0c;如 VNC、RDP 和 SSH。您可以使用任何现代 web 浏览器连接到您的桌面环境&#xff0c;而无需安装额外的软件。使用 Docker Compose 部署 Guacamole&#xff0c;如果没有docker-compose请先执行su…...

.baxia勒索病毒来袭:数据恢复与防护措施详解

导言 在当今这个信息化高速发展的时代&#xff0c;数据已成为企业和个人的核心资产&#xff0c;其价值不可估量。然而&#xff0c;随着网络技术的不断进步&#xff0c;网络安全威胁也日益严峻&#xff0c;其中勒索病毒作为一种新型的网络攻击手段&#xff0c;尤其是.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 是一款突破性的灵活应变无线电平台&#xff0c;可强化数字前端 &#xff08;DFE&#xff09;&#xff0c;用于 5G 大规模无线电部署和广泛的其他射频应用。 Zynq RFSoC DFE 基于唯一经过生产验证的自适应单芯片无线电…...

Ubuntu学习笔记 - Day3

文章目录 学习目标&#xff1a;学习内容&#xff1a;学习笔记&#xff1a;vim简介vim键盘图工作模式 vim移动光标操作上下左右移动翻页 vim替换和删除操作替换删除 vim插入模式详解进入模式搜索 vim底行模式操作保存退出行号 学习目标&#xff1a; 一周掌握 Linux基本使用技巧 …...

scala list系列

dd list:有序的&#xff0c;链表 1.建立 不可变列表 2.通过下标来访问&#xff1a;下标从0开始 3.不能修改 4.添加 5.删除 6.合并 7.查找&#xff0c;判断元素是否存在 8.遍历...

TLS协议基本原理与Wireshark分析_wireshark分析tls协议

01****背 景 随着车联网的迅猛发展&#xff0c;汽车已经不再是传统的机械交通工具&#xff0c;而是智能化、互联化的移动终端。然而&#xff0c;随之而来的是对车辆通信安全的日益严峻的威胁。在车联网生态系统中&#xff0c;车辆通过无线网络与其他车辆、基础设施以及云端服务…...

【359】基于springboot的智慧草莓基地管理系统

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本智慧草莓基地管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据…...

【智能算法应用】遗传算法求解车间布局优化问题

摘要 本文研究了基于遗传算法&#xff08;Genetic Algorithm, GA&#xff09;的车间布局优化方法。遗传算法是一种基于自然选择和遗传机制的优化算法&#xff0c;通过编码布局方案、交叉和变异操作生成新的布局个体&#xff0c;选择最优的车间布局方案。实验结果表明&#xff…...

java 中List 的使用

List集合是Collection接口的子接口&#xff0c;其下有两个实现类分别为ArrayList和 LinkedList List是一个接口&#xff0c;不能用new创建对象&#xff0c;需要用 ArrayList类 和 LinkedList类 来创建 特点 有序&#xff1a;存储元素的顺序和取出元素的顺序一致可以重复&…...

CSS学习之Grid网格布局基本概念、容器属性

网格布局 网格布局&#xff08;Grid&#xff09;是将网页划分成一个个网格单元&#xff0c;可任意组合不同的网格&#xff0c;轻松实现各种布局效果&#xff0c;也是目前CSS中最强大布局方案&#xff0c;比Flex更强大。 基本概念 容器和项目 当一个 HTML 元素将 display 属性…...

前后端交互接口(二)

前后端交互接口&#xff08;二&#xff09; 前言 在上一集我们约定了我们前后端交互接口的三条规则。这一集我们就先来看一看我们的一些proto文件。 浅看proto文件 在看文件之前&#xff0c;还是简单谈谈Protobuf Protobuf通过一个.proto文件定义数据结构&#xff0c;这个…...

HarmonyOs DevEco Studio小技巧28--部分鸿蒙生命周期详解

目录 前言 页面和自定义组件生命周期 页面生命周期 onPageShow --- 表示页面已经显示 onPageHide --- 表示页面已经隐藏 onBackPress --- 表示用户点击了返回键 组件生命周期 aboutToAppear --- 表示组件即将出现 onDidBuild --- 表示组件已经构建完成 aboutToDisap…...

STM32(hal库)的msp初始化HAL_TIM_Base_MspInit有什么用?为什么单独设置这个,而不是在timer_init()函数里直接初始化?

在STM32 HAL库中&#xff0c;HAL_TIM_Base_MspInit 函数是一个与定时器&#xff08;TIM&#xff09;相关的底层初始化函数&#xff0c;其名称中的 "Msp" 代表 MCU Service Package&#xff08;微控制器服务包&#xff09;&#xff0c;这是HAL库的一部分&#xff0c;用…...

三品PLM系统如何规范企业图纸文档资料电子化管理

三品PLM系统如何规范企业图纸文档资料电子化管理 图纸文档是企业设计、生产、管理的重要信息载体&#xff0c;是产品设计与生产维护的关键。传统纸质归档已无法满足现代需求&#xff0c;电子化管理成为提升效率和文档一致性的重要手段。然而&#xff0c;许多企业在实施电子化管…...

鸿蒙开发:arkts 如何读取json数据

为了支持ArkTS语言的开发&#xff0c;华为提供了完善的工具链&#xff0c;包括代码编辑器、编译器、调试器、测试工具等。开发者可以使用这些工具进行ArkTS应用的开发、调试和测试。同时&#xff0c;华为还提供了DevEco Studio这一一站式的开发平台&#xff0c;为运行在Harmony…...

Java学习篇之JVM 调优

Java学习篇之JVM 调优 一、JVM 是什么&#xff1f;二、JVM 官方参数建议三、JVM调优的场景四、如何监控JVM五、JVM调优的流程步骤1. 明确优化目标2. 监控和分析3. 确定调优参数4. 实施调优策略5. 持续观察和调整6. 定期评估和优化 一、JVM 是什么&#xff1f; JVM&#xff0c;…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

在Ubuntu中设置开机自动运行(sudo)指令的指南

在Ubuntu系统中&#xff0c;有时需要在系统启动时自动执行某些命令&#xff0c;特别是需要 sudo权限的指令。为了实现这一功能&#xff0c;可以使用多种方法&#xff0c;包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法&#xff0c;并提供…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合

在汽车智能化的汹涌浪潮中&#xff0c;车辆不再仅仅是传统的交通工具&#xff0c;而是逐步演变为高度智能的移动终端。这一转变的核心支撑&#xff0c;来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒&#xff08;T-Box&#xff09;方案&#xff1a;NXP S32K146 与…...

C++:多态机制详解

目录 一. 多态的概念 1.静态多态&#xff08;编译时多态&#xff09; 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1&#xff09;.协变 2&#xff09;.析构函数的重写 5.override 和 final关键字 1&#…...

免费PDF转图片工具

免费PDF转图片工具 一款简单易用的PDF转图片工具&#xff0c;可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件&#xff0c;也不需要在线上传文件&#xff0c;保护您的隐私。 工具截图 主要特点 &#x1f680; 快速转换&#xff1a;本地转换&#xff0c;无需等待上…...

AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机

这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机&#xff0c;因为在使用过程中发现 Airsim 对外部监控相机的描述模糊&#xff0c;而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置&#xff0c;最后在源码示例中找到了&#xff0c;所以感…...