当前位置: 首页 > 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;…...

19c补丁后oracle属主变化,导致不能识别磁盘组

补丁后服务器重启&#xff0c;数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后&#xff0c;存在与用户组权限相关的问题。具体表现为&#xff0c;Oracle 实例的运行用户&#xff08;oracle&#xff09;和集…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3

一&#xff0c;概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本&#xff1a;2014.07&#xff1b; Kernel版本&#xff1a;Linux-3.10&#xff1b; 二&#xff0c;Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01)&#xff0c;并让boo…...

Java线上CPU飙高问题排查全指南

一、引言 在Java应用的线上运行环境中&#xff0c;CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时&#xff0c;通常会导致应用响应缓慢&#xff0c;甚至服务不可用&#xff0c;严重影响用户体验和业务运行。因此&#xff0c;掌握一套科学有效的CPU飙高问题排查方法&…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.

ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #&#xff1a…...

pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)

目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 &#xff08;1&#xff09;输入单引号 &#xff08;2&#xff09;万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...

es6+和css3新增的特性有哪些

一&#xff1a;ECMAScript 新特性&#xff08;ES6&#xff09; ES6 (2015) - 革命性更新 1&#xff0c;记住的方法&#xff0c;从一个方法里面用到了哪些技术 1&#xff0c;let /const块级作用域声明2&#xff0c;**默认参数**&#xff1a;函数参数可以设置默认值。3&#x…...

边缘计算网关提升水产养殖尾水处理的远程运维效率

一、项目背景 随着水产养殖行业的快速发展&#xff0c;养殖尾水的处理成为了一个亟待解决的环保问题。传统的尾水处理方式不仅效率低下&#xff0c;而且难以实现精准监控和管理。为了提升尾水处理的效果和效率&#xff0c;同时降低人力成本&#xff0c;某大型水产养殖企业决定…...

leetcode_69.x的平方根

题目如下 &#xff1a; 看到题 &#xff0c;我们最原始的想法就是暴力解决: for(long long i 0;i<INT_MAX;i){if(i*ix){return i;}else if((i*i>x)&&((i-1)*(i-1)<x)){return i-1;}}我们直接开始遍历&#xff0c;我们是整数的平方根&#xff0c;所以我们分两…...

Redis上篇--知识点总结

Redis上篇–解析 本文大部分知识整理自网上&#xff0c;在正文结束后都会附上参考地址。如果想要深入或者详细学习可以通过文末链接跳转学习。 1. 基本介绍 Redis 是一个开源的、高性能的 内存键值数据库&#xff0c;Redis 的键值对中的 key 就是字符串对象&#xff0c;而 val…...