[大师C语言(第十七篇)]C语言链表背后技术详解

引言

链表是一种常见的数据结构,用于存储线性数据集合。在C语言中,链表由一系列节点组成,每个节点包含数据和指向下一个节点的指针。本文将深入探讨C语言链表背后的技术原理,并通过丰富的代码示例来讲解其应用。

第一部分:链表基础

1.1 链表的定义

链表是一种线性数据结构,由一系列节点组成,每个节点包含数据和指向下一个节点的指针。链表分为单向链表、双向链表和循环链表。

typedef struct Node {int data;struct Node *next;
} Node;

在上面的代码中,我们定义了一个名为Node的结构体,它包含一个整数类型的数据和一个指向Node类型的指针。

1.2 单向链表

单向链表是最简单的链表类型,每个节点只包含数据和指向下一个节点的指针。

Node *head = NULL;
Node *new_node = (Node *)malloc(sizeof(Node));
new_node->data = 42;
new_node->next = head;
head = new_node;

在上面的代码中,我们创建了一个单向链表,并添加了一个新节点。新节点的数据为42,指向链表的头部。

1.3 双向链表

双向链表每个节点包含数据、前一个节点的指针和后一个节点的指针。

typedef struct Node {int data;struct Node *prev;struct Node *next;
} Node;

在上面的代码中,我们定义了一个名为Node的结构体,它包含一个整数类型的数据、一个指向Node类型的前一个节点的指针和一个指向Node类型的后一个节点的指针。

1.4 循环链表

循环链表是最后一个节点的指针指向第一个节点的链表。

Node *head = NULL;
Node *new_node = (Node *)malloc(sizeof(Node));
new_node->data = 42;
new_node->next = head;
head = new_node;
new_node->next = head; // 循环链表操作

在上面的代码中,我们创建了一个循环链表,并添加了一个新节点。新节点的数据为42,指向链表的头部。然后,我们使新节点的next指针指向头部,形成循环链表。

总结

本文介绍了C语言链表的基础知识。通过本文的学习,读者可以了解到链表的定义、单向链表、双向链表和循环链表的概念。在下一部分,我们将深入探讨C语言链表的高级应用和实现原理。

第二部分:链表的高级应用

在第一部分中,我们已经了解了C语言链表的基础知识。在本部分,我们将进一步探讨链表的一些高级应用,包括链表操作、链表遍历和链表排序,并通过具体的代码示例来讲解这些高级应用。

2.1 链表操作

链表操作包括创建链表、添加节点、删除节点和修改节点。

#include <stdio.h>
#include <stdlib.h>typedef struct Node {int data;struct Node *next;
} Node;Node *create_list() {Node *head = NULL;Node *new_node = (Node *)malloc(sizeof(Node));new_node->data = 42;new_node->next = head;head = new_node;return head;
}Node *add_node(Node *head, int data) {Node *new_node = (Node *)malloc(sizeof(Node));new_node->data = data;new_node->next = head;head = new_node;return head;
}Node *delete_node(Node *head, int data) {Node *current = head;Node *previous = NULL;while (current != NULL && current->data != data) {previous = current;current = current->next;}if (current == NULL) {return head;}if (previous == NULL) {head = current->next;} else {previous->next = current->next;}free(current);return head;
}Node *modify_node(Node *head, int old_data, int new_data) {Node *current = head;while (current != NULL) {if (current->data == old_data) {current->data = new_data;return head;}current = current->next;}return head;
}

在上面的代码中,我们定义了几个函数来操作链表,包括创建链表、添加节点、删除节点和修改节点。

2.2 链表遍历

链表遍历是指按照链表的顺序访问每个节点,以获取链表中的数据。

void print_list(Node *head) {Node *current = head;while (current != NULL) {printf("%d ", current->data);current = current->next;}printf("\n");
}

在上面的代码中,我们定义了一个名为print_list的函数,它按照链表的顺序访问每个节点,并打印出节点的数据。

2.3 链表排序

链表排序是指按照一定的顺序对链表中的数据进行排序。

Node *sort_list(Node *head) {Node *current = head;Node *sorted_head = NULL;Node *sorted_current = NULL;while (current != NULL) {Node *next = current->next;if (sorted_head == NULL || current->data <= sorted_head->data) {if (sorted_current == NULL) {sorted_head = current;} else {sorted_current->next = current;sorted_current = current;}} else {Node *previous = sorted_head;while (previous->next != NULL && current->data > previous->next->data) {previous = previous->next;}if (previous->next == NULL) {previous->next = current;} else {current->next = previous->next;previous->next = current;}}current = next;}return sorted_head;
}

在上面的代码中,我们定义了一个名为sort_list的函数,它按照升序对链表中的数据进行排序。

2.4 链表的插入与删除操作

链表的插入和删除操作是链表操作中非常常见且重要的操作。

插入操作

插入操作可以分为在链表头部插入、在链表尾部插入和在链表中间插入。

Node *insert_at_head(Node *head, int data) {Node *new_node = (Node *)malloc(sizeof(Node));new_node->data = data;new_node->next = head;head = new_node;return head;
}Node *insert_at_tail(Node *head, int data) {Node *new_node = (Node *)malloc(sizeof(Node));new_node->data = data;new_node->next = NULL;if (head == NULL) {head = new_node;return head;}Node *current = head;while (current->next != NULL) {current = current->next;}current->next = new_node;return head;
}Node *insert_at_middle(Node *head, int data, int position) {Node *new_node = (Node *)malloc(sizeof(Node));new_node->data = data;new_node->next = NULL;if (head == NULL) {head = new_node;return head;}if (position == 0) {return insert_at_head(head, data);}Node *current = head;int count = 0;while (current != NULL && count < position - 1) {current = current->next;count++;}if (current == NULL) {return head;}new_node->next = current->next;current->next = new_node;return head;
}

删除操作

删除操作可以分为删除链表头部节点、删除链表尾部节点和删除链表中间节点。

Node *delete_at_head(Node *head) {if (head == NULL) {return NULL;}Node *temp = head;head = head->next;free(temp);return head;
}Node *delete_at_tail(Node *head) {if (head == NULL || head->next == NULL) {Node *temp = head;head = NULL;free(temp);return NULL;}Node *current = head;while (current->next->next != NULL) {current = current->next;}Node *temp = current->next;current->next = NULL;free(temp);return head;
}Node *delete_at_middle(Node *head, int position) {if (head == NULL) {return NULL;}if (position == 0) {return delete_at_head(head);}Node *current = head;int count = 0;while (current != NULL && count < position - 1) {current = current->next;count++;}if (current == NULL || current->next == NULL) {return head;}Node *temp = current->next;current->next = current->next->next;free(temp);return head;
}

2.5 链表的合并与拆分

链表的合并与拆分操作在某些应用场景中也非常有用。

合并操作

合并操作可以将两个链表合并为一个链表。

Node *merge_lists(Node *l1, Node *l2) {if (l1 == NULL) {return l2;}if (l2 == NULL) {return l1;}if (l1->data < l2->data) {l1->next = merge_lists(l1->next, l2);return l1;} else {l2->next = merge_lists(l1, l2->next);return l2;}
}

在上面的代码中,我们定义了一个名为merge_lists的函数,它将两个链表按照数据值的大小顺序合并成一个新链表。

拆分操作

拆分操作可以将一个链表拆分为两个链表。

Node *split_list(Node *head, int position) {if (head == NULL) {return NULL;}Node *slow = head;Node *fast = head;int count = 0;while (fast != NULL && count < position) {fast = fast->next;count++;}if (fast == NULL) {return NULL;}Node *second_list = fast->next;fast->next = NULL;return second_list;
}

在上面的代码中,我们定义了一个名为split_list的函数,它将链表在指定位置拆分为两个链表。

2.6 链表的逆序与反转

链表的逆序和反转操作可以将链表中的数据顺序进行反转。

逆序操作

逆序操作可以逆序链表中的数据。

Node *reverse_list(Node *head) {Node *prev = NULL;Node *current = head;Node *next = NULL;while (current != NULL) {next = current->next;current->next = prev;prev = current;current = next;}return prev;
}

在上面的代码中,我们定义了一个名为reverse_list的函数,它将链表中的数据逆序。

反转操作

反转操作可以反转链表中节点的方向。

Node *reverse_nodes(Node *head) {Node *prev = NULL;Node *current = head;Node *next = NULL;while (current != NULL) {next = current->next;current->next = prev;prev = current;current = next;}return prev;
}

在上面的代码中,我们定义了一个名为reverse_nodes的函数,它将链表中节点的方向进行反转。

总结

在本部分中,我们介绍了C语言链表的一些高级应用,包括链表操作、链表遍历、链表排序、链表的合并与拆分、链表的逆序与反转等。通过这些高级应用,我们可以更好地控制链表的行为和性能。在下一部分,我们将深入探讨C语言链表的实现原理和底层技术细节。

第三部分:链表的实现细节

在前两部分中,我们学习了C语言链表的基础知识和高级应用。在本部分,我们将深入探讨链表的实现细节。

3.1 内存分配与释放

链表的底层实现涉及到内存的分配和释放。链表节点通常是通过动态内存分配(如malloc)创建的,当不再需要节点时,应该使用free函数释放内存,以避免内存泄漏。

Node *create_node(int data) {Node *new_node = (Node *)malloc(sizeof(Node));if (new_node == NULL) {// 处理内存分配失败的情况return NULL;}new_node->data = data;new_node->next = NULL;return new_node;
}void delete_node(Node *node) {if (node == NULL) {return;}free(node);node = NULL;
}

在上面的代码中,我们定义了创建和删除链表节点的函数。创建节点时,我们使用malloc分配内存,并检查分配是否成功。如果成功,我们初始化节点的数据和指针,并返回新节点。删除节点时,我们使用free释放内存,并设置节点指针为NULL以避免野指针。

3.2 指针操作

链表的底层实现依赖于指针操作。指针用于指向链表节点,实现节点的连接和数据访问。

void insert_at_head(Node **head, int data) {Node *new_node = create_node(data);if (new_node == NULL) {// 处理创建节点失败的情况return;}new_node->next = *head;*head = new_node;
}void insert_at_tail(Node **head, int data) {Node *new_node = create_node(data);if (new_node == NULL) {// 处理创建节点失败的情况return;}if (*head == NULL) {*head = new_node;return;}Node *current = *head;while (current->next != NULL) {current = current->next;}current->next = new_node;
}

在上面的代码中,我们定义了在链表头部和尾部插入节点的函数。这些函数使用指针操作来找到链表的头部或尾部,并插入新节点。

3.3 数据访问

链表的底层实现还涉及到数据访问。链表节点中的数据可以通过指针访问。

int get_data(Node *node) {if (node == NULL) {// 处理空节点的情况return -1;}return node->data;
}

在上面的代码中,我们定义了一个函数来获取链表节点的数据。这个函数首先检查节点是否为NULL,以避免访问空节点的风险。

3.4 循环与条件判断

链表的底层实现中经常使用循环和条件判断来遍历链表或执行特定操作。

void print_list(Node *head) {Node *current = head;while (current != NULL) {printf("%d ", current->data);current = current->next;}printf("\n");
}

在上面的代码中,我们定义了一个函数来打印链表中的数据。这个函数使用循环来遍历链表,并使用条件判断来确保循环不会在到达链表末尾时继续执行。

3.5 总结

通过本部分的学习,我们了解了C语言链表的实现细节。链表的实现依赖于内存分配与释放、指针操作、数据访问、循环与条件判断等底层技术。正确理解和使用这些技术是编写高效、可靠的链表程序的关键。随着技术的发展,链表的实现将继续为C语言编程带来更多的可能性和创新。

总结

本文详细介绍了C语言链表的基础知识、高级应用以及底层实现细节。通过本文的学习,读者可以了解到链表的定义、单向链表、双向链表和循环链表的概念,以及链表操作、链表遍历、链表排序等高级应用。此外,我们还深入探讨了链表的底层实现细节,包括内存分配与释放、指针操作、数据访问、循环与条件判断等。

在实际编程中,正确使用链表可以提高程序的性能和可维护性。通过掌握链表的基础知识和高级应用,我们可以更好地控制链表的行为和性能。同时,了解链表的底层实现细节有助于我们编写高效、可靠的链表程序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/20110.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

北斗应急救援终端如何做好汛期重点行业安全防控?

【安全提示】 汛期各地高温多雨、极端天气增多 防汛和安全生产形势严峻复杂如何做好汛期重点行业企业安全生产风险防控&#xff1f; 顶坚北斗短报文终端V1单北斗定位终端 北斗应急救援终端在汛期重点行业安全防控中扮演着关键角色&#xff0c;其高可靠性、稳定性和丰富的功能扩…

达摩院AI早癌筛查技术闪耀联合国,癌症早治时代来临?

全文预计1200字左右&#xff0c;预计阅读需要6分钟。 5月30日&#xff0c;在日内瓦举行的联合国AI大会上&#xff0c;各国的代表性企业拿出最优秀的AI成果进行分享。其中代表中国的是阿里巴巴集团的下属机构-达摩院&#xff0c;其在现场播放的一段影像&#xff0c;让在场的所有…

uniApp子组件监听数据的变化的方法之一

props:{//用来接收外界传递过来的数据swiperList:{type:Array,default:[]}}, swiperList&#xff1a;是父组件传递过来的值 通过 watch 监听&#xff08;在父组件中也同样可以使用&#xff0c;跟VUE的监听数据变化同理&#xff09; watch:{//监听组件中的数据变化swiperList(ol…

Diffusion Facial Forgery (DiFF) ——一个新的大规模人脸伪造检测数据集

1. 概述 近年来&#xff0c;条件扩散模型&#xff08;CDM&#xff09;在图像生成领域备受关注。它能够通过简单的输入&#xff08;如自然语言提示&#xff09;生成令人惊讶的忠实图像。然而&#xff0c;这一进步也引发了新的安全和隐私问题。例如&#xff0c;怀有恶意的个人现…

【笔记】使用XtraBackup进行热备份

备份环境&#xff1a; 具备dockermysql8(5.7及以下版本更换xtrabackup版本即可&#xff0c;具体版本号查看官网)将云盘挂载到服务器上&#xff0c;可以使用s3协议 #!/bin/bash# 目录配置部分 HOST_BACKUP_DIR"/root/docker/mysql8/backup/full" # 宿主机备份目录 H…

结构体(C保姆级讲解)

前言&#xff1a; 为什么会有结构体&#xff0c;结构体可以用来面熟一个复杂对象&#xff0c;我们知道C语言中有哪些数据类型&#xff0c;有整型&#xff0c;有浮点型&#xff0c;有字符型&#xff0c;但是在生活中&#xff0c;我们需要描述一些比较复杂的东西&#xff0c;比如…

如何理解央行买卖国债?

浙商证券覃汉认为&#xff0c;央行对长债的风险持续关注&#xff0c;30年国债收益率较难突破2.5%&#xff0c;区间底部已经多次印证&#xff0c;在学习效应影响下&#xff0c;长端利率预计继续以震荡调整为主。 1、央行买卖国债的政策要求、历史经验、优势 2023年中央金融工作…

语音助手拦截,拦截小秘书

呼叫中心业务场景下会遇到很多的语音助手和语音小秘书&#xff0c;还有一些漏话提醒、语音信箱等&#xff1b;大部分原因是由于主叫号码标记问题导致的局端和终端拦截策略&#xff0c;电话没有真实有效的触达并产生了通信费&#xff0c;这让很多业务场景下通信成本上涨据不完全…

常用中间件各版本下载

常用中间件下载地址 前言分布式中间件负载均衡中间件缓存中间件数据库中间件其他中间件1、Maven下载地址2、Git下载地址2、JDK下载地址3、MySQL下载地址4、Redis下载地址5、Nacos下载地址6、Tomcat下载地址7、Nginx下载地址8、RocketMQ下载地址8、RabbitMQ下载地址8、Erlang下载…

【Redis】常见的 Redis 集群方案

Redis 集群用于在多个 Redis 节点之间分布数据&#xff0c;以提高可用性和扩展性。常见的 Redis 集群方案有以下几种&#xff1a; 1. 哨兵 (Sentinel) Sentinel 是一种高可用解决方案&#xff0c;用于监控 Redis 主从复制的实例并在主节点发生故障时进行自动故障转移。 优点…

Amazon云计算AWS(三)

目录 五、关系数据库服务RDS&#xff08;一&#xff09;RDS的基本原理&#xff08;二&#xff09;RDS的使用 六、简单队列服务SQS&#xff08;一&#xff09;SQS的基本模型&#xff08;二&#xff09;SQS的消息 七、内容推送服务CloudFront&#xff08;一&#xff09;CDN&#…

【硬件工程师话家常】新人硬件工程师,工作中遇到的问题list

新人硬件工程师能够通过面试&#xff0c;已经证明是能够胜任硬件工程师职责&#xff0c;当然胜任的时间会延迟&#xff0c;而不是当下&#xff0c;为什么呢&#xff1f;因为学校学习和公司做产品&#xff0c;两者之间有差异&#xff0c;会需要适应期。今天来看看新人硬件工程师…

Python之Mammoth库的详解和使用

Mammoth 旨在转换 .docx 文档&#xff08;例如由 Microsoft Word、Google Docs 和 LibreOffice 创建的文档&#xff09;并将其转换为 HTML。Mammoth 旨在通过使用文档中的语义信息并忽略其他细节来生成简单干净的 HTML。例如&#xff0c;Mammoth 将任何具有样式的段落转换Headi…

Vue3-路由详解

文章目录 路由对路由的理解安装 Vue Router基本切换效果两个注意点路由器工作模式to的两种写法命名路由嵌套路由路由传参query参数params参数 路由的props配置replace属性编程式导航重定向 更多相关内容可查看 路由 附git地址&#xff1a;https://gitee.com/its-a-little-bad/…

C前端与Web前端:深入解析二者之间的区别

C前端与Web前端&#xff1a;深入解析二者之间的区别 在编程领域中&#xff0c;C前端与Web前端常常被提及&#xff0c;但这两者之间究竟有何不同&#xff1f;本文将通过四个方面、五个方面、六个方面和七个方面&#xff0c;深入剖析C前端与Web前端之间的区别&#xff0c;帮助读…

项目纪实 | 版本升级操作get!GreatDB分布式升级过程详解

某客户项目现场&#xff0c;因其业务系统要用到数据库新版本中的功能特性&#xff0c;因此考虑升级现有数据库版本。在升级之前&#xff0c;万里数据库项目团队帮助客户在本地测试环境构造了相同的基础版本&#xff0c;导入部分生产数据&#xff0c;尽量复刻生产环境进行升级&a…

电脑缺少运行库,无法启动程序

在我们使用一些软件的时候&#xff0c;由于电脑缺少一些运行库&#xff0c;导致无法启动应用软件&#xff0c;此时需要我们安装缺少的运行库。 比如当电脑提示&#xff1a; Cannot load library Qt5Xlsx.dll 我们就需要下载C得运行库&#xff0c;以满足软件运行需要。 下载链…

某三甲医院智能化系统建设项目施工组织设计(516页)

第十四节、ICU重症监护探视系统设计方案 1、系统总体概述 某市市第一人民医院为一个集医疗、研究、医学教学为一体现代化医院建筑群&#xff0c;不仅在医学界的学术地位和声誉&#xff0c;还应拥有赋予人性的医疗环境&#xff0c;为病人创造最舒适的医疗条件。 探视系统帮助…

如何查看谁连接到了你的Wi-Fi网络?这里提供几种方法或工具

序言 你知道谁连接到你路由器的Wi-Fi网络吗?查看从路由器或计算机连接到Wi-Fi网络的设备列表,找出答案。 请记住,现在很多设备都可以连接到了你的Wi-Fi,该名单包括笔记本电脑、智能手机、平板电脑、智能电视、机顶盒、游戏机、Wi-Fi打印机等。 使用GlassWire Pro查看连接…

chatMed开发日志博客(持续更新中)

目录 1. 项目概述 2. 开发人员团队 3. 大致需求 4. 开发内容 4.1. 前端开发 4.1.1: 前端页面开发 4.1.2: 登录机制以及路由守卫的开发 4.1.3: 文件上传机制和保存机制 4.1.4: 消息传递机制 4.2. 线程池开发 4.3. 在线调试 1. 项目概述 搭建一个基于深度学习的分析平台…