C语言之链表详解

article/2025/9/14 12:38:02

目录

一、链表定义

二、链表分类

三、链表操作

四、单向链表

1.链表定义

2.插入操作

3.删除操作

4.修改操作

5.查找操作

五、双向链表

1.链表定义

2.插入操作

3.删除操作

4.修改操作

5.查找操作


一、链表定义

        链表是一种基本的数据结构,它由一系列节点组成,每个节点包含一个值和指向下一个节点的指针。链表的特点是可以动态添加和删除节点,而不需要预先知道数据的数量。与数组不同,链表中的节点不一定是连续的存储空间,因此可以有效地利用内存空间。

二、链表分类

链表可以分为三种类型:

  • 单向链表:每个节点只有一个指针指向下一个节点,最后一个节点的指针指向NULL。
  • 双向链表:每个节点有两个指针,分别指向上一个节点和下一个节点,可以在O(1)时间内实现向前和向后遍历。
  • 循环链表:在单向或双向链表的基础上,将最后一个节点的指针指向头节点,形成一个环。

三、链表操作

链表的操作包括插入、删除、查找、遍历等。

  • 插入操作:可以在链表头或尾插入节点,也可以在指定位置插入节点。
  • 删除操作:可以删除指定节点或按照值删除节点。
  • 查找操作:可以查找指定节点或按照值查找节点。
  • 遍历操作:可以遍历整个链表,输出每个节点的值或执行其他操作。

        链表的优点是可以动态添加和删除节点,因此非常适用于需要频繁插入和删除数据的场景。链表的缺点是访问操作的时间复杂度为O(n),而且需要额外的空间存储节点的指针,因此在需要频繁访问数据的场景中,效率可能不如数组。

        链表在计算机科学中有广泛的应用,例如操作系统中的进程链表、文件系统中的目录链表、图论中的邻接表等。在C语言中,链表常常用于实现动态内存分配、函数调用栈、多项式运算等问题。

四、单向链表

        单向链表是由若干个节点组成的数据结构,每个节点包含两个部分:数据域和指针域。数据域存储节点的数据,指针域存储下一个节点的地址。

413c6e745fc63630140e7364e5c3bcf4.jpeg

1.链表定义

单向链表定义如下:

struct ListNode {int val;struct ListNode* next;
};

2.插入操作

链表的插入操作可以在链表的头部、尾部或指定位置插入节点。具体实现如下:

// 在头部插入节点
struct ListNode* insertAtHead(struct ListNode* head, int val) {struct ListNode* new_node = (struct ListNode*)malloc(sizeof(struct ListNode));new_node->val = val;new_node->next = head;return new_node;
}// 在尾部插入节点
struct ListNode* insertAtTail(struct ListNode* head, int val) {struct ListNode* new_node = (struct ListNode*)malloc(sizeof(struct ListNode));new_node->val = val;new_node->next = NULL;if (head == NULL) {return new_node;}struct ListNode* p = head;while (p->next != NULL) {p = p->next;}p->next = new_node;return head;
}// 在指定位置插入节点
struct ListNode* insertAtIndex(struct ListNode* head, int index, int val) {int i = 0;struct ListNode* new_node = (struct ListNode*)malloc(sizeof(struct ListNode));new_node->val = val;if (index == 0) {new_node->next = head;return new_node;}struct ListNode* p = head;while (i < index - 1 && p != NULL) {p = p->next;i++;}if (p == NULL) {free(new_node);return head;}new_node->next = p->next;p->next = new_node;return head;
}

3.删除操作

链表的删除操作可以删除指定位置或指定值的节点。具体实现如下:

// 删除指定位置的节点
struct ListNode* deleteAtIndex(struct ListNode* head, int index) {int i = 0;if (head == NULL) {return NULL;}if (index == 0) {struct ListNode* temp = head;head = head->next;free(temp);return head;}struct ListNode* p = head;while (i < index - 1 && p != NULL) {p = p->next;i++;}if (p == NULL || p->next == NULL) {return head;}struct ListNode* temp = p->next;p->next = p->next->next;free(temp);return head;
}
// 删除指定值的节点
struct ListNode* deleteNode(struct ListNode* head, int val) {struct ListNode* p = head;struct ListNode* prev = NULL;while (p != NULL && p->val != val) {prev = p;p = p->next;}if (p == NULL) {return head;}if (prev == NULL) {head = head->next;} else {prev->next = p->next;}free(p);return head;
}

4.修改操作

链表的修改操作可以修改指定位置或指定值的节点的值。具体实现如下:

// 修改指定位置的节点的值
struct ListNode* modifyAtIndex(struct ListNode* head, int index, int val) {struct ListNode* p = head;int i = 0;while (i < index && p != NULL) {p = p->next;i++;}if (p == NULL) {return head;}p->val = val;return head;
}// 修改指定值的节点的值
struct ListNode* modifyNode(struct ListNode* head, int old_val, int new_val) {struct ListNode* p = head;while (p != NULL && p->val != old_val) {p = p->next;}if (p == NULL) {return head;}p->val = new_val;return head;
}

5.查找操作

链表的查找操作可以查找指定位置或指定值的节点。具体实现如下:

// 查找指定位置的节点的值
int getValAtIndex(struct ListNode* head, int index) {struct ListNode* p = head;int i = 0;while (i < index && p != NULL) {p = p->next;i++;}if (p == NULL) {return -1;}return p->val;
}// 查找指定值的节点的位置
int getIndexByVal(struct ListNode* head, int val) {struct ListNode* p = head;int i = 0;while (p != NULL && p->val != val) {p = p->next;i++;}if (p == NULL) {return -1;}return i;
}

        以上是链表的基本操作,实现时需要注意空指针和越界等问题。

五、双向链表

        双向链表和单向链表的不同在于每个节点有两个指针,分别指向前驱节点和后继节点。双向链表的优点是可以实现双向遍历和O(1)的删除操作,缺点是每个节点需要额外的一个指针。

20200726124645552.png

 1.链表定义

双向链表定义如下:

struct ListNode {int val;struct ListNode* prev;struct ListNode* next;
};

2.插入操作

双向链表的插入操作可以在指定位置前面或后面插入节点,具体实现如下:

// 在指定位置前面插入节点
struct ListNode* insertBefore(struct ListNode* head, int index, int val) {struct ListNode* p = head;int i = 0;while (i < index && p != NULL) {p = p->next;i++;}if (p == NULL) {return head;}struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode));node->val = val;node->prev = p->prev;node->next = p;if (p->prev != NULL) {p->prev->next = node;} else {head = node;}p->prev = node;return head;
}// 在指定位置后面插入节点
struct ListNode* insertAfter(struct ListNode* head, int index, int val) {struct ListNode* p = head;int i = 0;while (i < index && p != NULL) {p = p->next;i++;}if (p == NULL) {return head;}struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode));node->val = val;node->prev = p;node->next = p->next;if (p->next != NULL) {p->next->prev = node;}p->next = node;return head;
}

3.删除操作

双向链表的删除操作可以删除指定位置或指定值的节点,具体实现如下:

// 删除指定位置的节点
struct ListNode* deleteAtIndex(struct ListNode* head, int index) {struct ListNode* p = head;int i = 0;while (i < index && p != NULL) {p = p->next;i++;}if (p == NULL) {return head;}if (p->prev != NULL) {p->prev->next = p->next;} else {head = p->next;}if (p->next != NULL) {p->next->prev = p->prev;}free(p);return head;
}// 删除指定值的节点
struct ListNode* deleteNode(struct ListNode* head, int val) {struct ListNode* p = head;while (p != NULL && p->val != val) {p = p->next;}if (p == NULL) {return head;}if (p->prev != NULL) {p->prev->next = p->next;} else {head = p->next;}if (p->next != NULL) {p->next->prev = p->prev;}free(p);return head;
}

4.修改操作

双向链表的修改操作可以直接修改指定节点的值,具体实现如下:

// 修改指定位置的节点值
struct ListNode* updateAtIndex(struct ListNode* head, int index, int val) {struct ListNode* p = head;int i = 0;while (i < index && p != NULL) {p = p->next;i++;}if (p == NULL) {return head;}p->val = val;return head;
}

5.查找操作

双向链表的查找操作可以按位置或按值查找,具体实现如下:

// 按位置查找节点
struct ListNode* getNodeAtIndex(struct ListNode* head, int index) {struct ListNode* p = head;int i = 0;while (i < index && p != NULL) {p = p->next;i++;}return p;
}// 按值查找节点
struct ListNode* getNodeByValue(struct ListNode* head, int val) {struct ListNode* p = head;while (p != NULL && p->val != val) {p = p->next;}return p;
}

以上是双向链表的增删改查操作的实现,可以根据需要进行调用。需要注意的是,在使用完链表后要及时释放内存,避免内存泄漏。

        如果觉得文章写的还不错,麻烦点赞,收藏加关注哦​!


        关于更多嵌入式C语言、FreeRTOS、RT-Thread、Linux应用编程、linux驱动等相关知识,关注公众号【嵌入式Linux知识共享】,后续精彩内容及时收看了解。

 


http://chatgpt.dhexx.cn/article/QhfOXf6f.shtml

相关文章

C语言 链表创建及操作

C语言 链表创建及操作 第一部分构建链表&#xff0c;定义结构体&#xff0c;分别用头插法、尾插法实现&#xff0c;这里封装了打印函数&#xff1a;printf();做练习方便后续使用&#xff1b;对链表进行查找&#xff0c;并将查找到的值构建一个新的链表&#xff1b;链表的转置&…

Netty权威指南 读书笔记

文档太大&#xff0c;PDF格式的存档已上传到百度网盘&#xff1a; 链接: https://pan.baidu.com/s/1QnUDWujGOXXCq5iWQBIRJg 提取码: q46s

Netty权威指南(第2版) pdf百度网盘下载

欢迎大家关注我的公众号【老周聊架构】&#xff0c;Java后端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。 链接: https://pan.baidu.com/s/1DfxG9qKU2fshi6ha1a8NkA 提取码: bmt4

Netty权威指南带目录完整版.pdf

2019独角兽企业重金招聘Python工程师标准>>> Netty权威指南带目录完整版.pdf 转载于:https://my.oschina.net/xiaojianyu/blog/3011828

Netty权威指南总结(三)

五、Netty实战技巧&#xff1a; (一) 多线程编程实践&#xff1a; 1. Netty中使用多线程的技巧&#xff1a; 创建两个NioEventLoopGroup&#xff0c;用于逻辑隔离NIO Acceptor和NIO IO线程。 尽量不要在ChannelHandler中启动用户线程&#xff08;解码后用于将POJO消息派发到后端…

Netty权威指南(四)TCP粘包/拆包问题

TCP粘包/拆包问题解决之道 上一章一、介绍1.1 TCP粘包/拆包问题说明1.2 TCP粘包/拆包发生的原因1.3 粘包问题的解决策略 二、未考虑TCP粘包导致的功能异常案例2.1 TimeServerHandler的改造2.2 TimeClientHandler的改造2.3 运行 三、利用LineBasedFrameDecoder解决TCP粘包问题3.…

《Netty权威指南》

《Netty权威指南》 基本信息 作者&#xff1a; 李林锋 出版社&#xff1a;电子工业出版社 ISBN&#xff1a;9787121233432 上架时间&#xff1a;2014-5-29 出版日期&#xff1a;2014 年6月 开本&#xff1a;16开 页码&#xff1a;524 版次&#xff1a;1-1 所属分类&#xff…

Netty权威指南~第一章Java的I/O演进之路

本章内容如下&#xff1a; 5种网络I/O模型的介绍I/O多路复用的介绍 1、I/O基础入门 在Java1.4之前&#xff0c;Java对I/O的支持不完善&#xff0c;开发人员在开发高性能I/O的程序时&#xff0c;会面临以下问题&#xff1a; 没有数据缓冲区&#xff0c;I/O性能存在问题没有C…

Netty权威指南(第2版)

网站 更多书籍点击进入>> CiCi岛 下载 电子版仅供预览及学习交流使用&#xff0c;下载后请24小时内删除&#xff0c;支持正版&#xff0c;喜欢的请购买正版书籍 电子书下载(皮皮云盘-点击“普通下载”)购买正版 封页 编辑推荐 1、Hadoop、Storm、Spark、Facebook、…

Netty权威指南——WebSocket协议开发

一、简介 由于HTTP协议的开销&#xff0c;导致他们不适于用于低延迟应用&#xff0c;为了解决这些问题&#xff0c;WebSocket将网络套接字引入到了客户端和服务端&#xff0c;浏览器和服务器之间可以通过套接字建立持久的连接&#xff0c;双方随时可以互发数据给对方&#xff…

netty权威指南 微云_《Netty权威指南》(二)NIO 入门

2.1 BIO 采用 BIO 通信模型的服务器&#xff0c;通常由一个独立的 Acceptor 线程负责监听客户端的连接&#xff0c;它接收到客户端连接请求之后为每个客户端创建一个新的线程进行处理&#xff0c;处理完成后&#xff0c;通过输出流返回应答给客户端&#xff0c;线程销毁。 grap…

netty 权威指南~第11章——WebSoket协议开发

本章主要学习内容如下&#xff1a; 1、WebSocket入门 2、Netty WebSocket协议开发 第节一&#xff1a;WebSocket入门 WebSocket是HTML5开始提供的一种浏览器与服务器进行全双通信的网络技术&#xff0c;WebSocket通信协议与2011年被IEIF定位标准RFC6455&#xff0c;WebsSock…

Netty权威指南 第2版

https://www.cnblogs.com/plxz/p/9910493.html 第一章  Java的I/O演进之路 1.I/O基础入门 1.Linux网络I/O模型简介 1.阻塞I/O模型&#xff1a;最常用的I/O模型&#xff0c;缺省情况下&#xff0c; 所有文件操作都是阻塞的 2.非阻塞I/O模型&#xff1a;recvform从应用层到内…

Netty权威指南总结(一)

一、为什么选择Netty&#xff1a; API使用简单&#xff0c;开发门槛低&#xff0c;屏蔽了NIO通信的底层细节。 功能强大&#xff0c;预制了很多种编解码功能&#xff0c;支持主流协议。 定制能力强&#xff0c;可以通过ChannelHandler对通信框架进行灵活地拓展。 性能高、成熟、…

Netty权威指南

Chapter1.java I/O演进之路 1.1I/O基础入门 在java 1.4之前&#xff0c;java程序员在开发高性能I/O程序的时候&#xff0c;会面临的问题主要有&#xff1a; 1.没有数据缓冲区&#xff0c;I/O性能存在问题 2.没有c或者c中的Channel概念&#xff0c;只有输入和输出流 3.同步…

三维视觉传感器的类型

三角法测量原理 视觉传感器的坐标系统 单一摄像机二维传感器 点结构光视觉传感器 线结构光视觉传感器 条纹结构光视觉传感器 条纹编码三维视觉传感器 彩色编码视觉传感器 被动双目视觉传感器 编码照明双目视觉传感器

传感器sensor

传感器分类 转换原理 传感器名称 典型应用 转换形式 中间参量 电 参 数 电 阻 移动电位器角点 改变电阻 电位器传感器 位移 改变电阻丝或片尺寸 电阻丝应变传感器、半导体应变传感器 微应变、力、负荷 利用电阻的温度效应 热丝传感器 气流速度、液体流量 电阻…

多任务多传感器数据融合实现3D目标检测

转载自&#xff1a;自动驾驶之心 01 引言 本文介绍一篇uber公司在CVPR上发表的一篇论文&#xff0c;即使用多种传感器&#xff08;LiDAR和RGB相机&#xff09;数据&#xff0c;以及多任务进行数据融合&#xff0c;实现准确高效的3D目标检测。简而言之&#xff0c;自动驾驶领域…

压力传感器

压力传感器 压力传感器是最常用的一种传感器&#xff0c;其应用范围有各种工业互通环境&#xff0c;涉及航空&#xff0c;航天&#xff0c;军工&#xff0c;石化&#xff0c;电力等。按照不同的测试&#xff0c;压力类型可分表压传感器&#xff0c;差压传感器&#xff0c;绝压…

多传感器融合定位(一)——3D激光里程计

目录 一、点云地图整体流程 二、激光里程计方案 2.1 ICP点到点 2.1.1 ICP推导 2.1.2 ICP改进 2.2 NDT 2.2.1 NDT推导 2.2.2 NDT改进 2.3 LOAM系 2.3.1 LOAM 2.3.2 A-LOAM 2.3.3 LEGO-LOAM 2.4 数据集及评价指标 2.4.1 KITTI简介 2.4.2 指标 一、点云地图整体流程…