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

C语言:单链表学习

文件:main.c

#include "linkedList.h"int main(int argc, char *argv[]) {// 创建头结点NODE *head = NULL;// 创建链表if (llist_create(&head, 666) < 0){perror("链表创建失败!");return -1;}// 向链表插入数据llist_addTail(&head, 777);llist_addTail(&head, 888);llist_addTail(&head, 999); // 666 777 888 999llist_addHead(&head, 7777);llist_addHead(&head, 8888);llist_addHead(&head, 9999); // 9999 8888 7777 666 777 888 999llist_insert(&head, 888, 1024); // 9999 8888 7777 666 777 1024 888 999// 测试遍历数据llist_showAll(head);// 更新数据llist_update(head, 999, 99999);llist_showAll(head); // 9999	8888	7777	666	777	1024	888	99999// 删除数据llist_delete(&head, 777);llist_showAll(head); // 9999	  8888	 7777  666	1024 888 99999// 销毁链表llist_destroy(&head);return 0;
}

文件:linkedList

#include "linkedList.h"/*** @brief 创建一个节点(私有函数)** @param head* @param data** @return*/
static NODE* __node_create(NODE **head, DATA data)
{// 创建一个新节点NODE *p = (NODE*)malloc(sizeof(NODE));// 校验是否创建成功if (!p) return NULL;// 初始化节点p->data = data; // 将外部插入的数据存储到当前节点的data成员p->next = NULL; // 节点默认指向NULLreturn p;
}/*** @brief 链表的创建(没有指定头结点的前提下进行(本案例涉及的是无头节点))* @param head 待操作链表,如果是改变链表结构,使用二级指针* @param data 待插入数据* @return 创建成功返回0,否则返回-1*/
int llist_create(NODE **head, DATA data)
{// 校验链表是否存在if (*head != NULL) return -1;// 创建一个新节点NODE *p = (NODE*)malloc(sizeof(NODE));// 校验是否创建成功if (!p) return -1;// 初始化节点p->data = data; // 将外部插入的数据存储到当前节点的data成员p->next = NULL; // 节点默认指向NULL// 将创建的新节点作为链表的头节点*head = p; // 就是让链表指针指向第一个节点return 0;
}/*** @brief 向链表头部插入一个节点数据(头插法)* @param head 待操作链表,如果是改变链表结构,使用二级指针* @param data 待插入数据* @return 创建成功返回0,否则返回-1*/
int llist_addHead(NODE **head, DATA data)
{// 创建一个新节点NODE *p = (NODE*)malloc(sizeof(NODE));// 校验是否创建成功if (!p) return -1;// 初始化节点p->data = data;  // 存放插入的数据p->next = *head; // 让头指针指向新创建的节点p// 将当前的新节点作为头节点(更新头指针)*head = p;return 0;
}/*** @brief 向链表尾部插入一个节点数据(尾插法)* @param head 待操作链表,如果是改变链表结构,使用二级指针* @param data 待插入数据* @return 创建成功返回0,否则返回-1*/
int llist_addTail(NODE **head, DATA data)
{// 创建一个新节点NODE *pNew = (NODE*)malloc(sizeof(NODE));// 校验是否创建成功if (!pNew) return -1;// 初始化节点pNew->data = data;pNew->next = NULL;// 接收链表NODE *p = *head, *q = NULL;// 如果*p不存在,说明这是一个空链表if (!p){*head = pNew; // 更新头指针,让头指针指向pNewreturn 0;}// 如果是非空链表,使用循环查找尾结点// 指针尾速法遍历链表while (p){q = p;      // q记录p偏移之前的位置p = p->next;// 指针偏移,得到尾结点}// 此时p指向NULL,q指向尾节点// 让原链表的尾节点指向pNewq->next = pNew;return 0;
}/*** @brief 向链表指定位置插入一个节点数据(中间插法)* @param head 待操作链表,如果是改变链表结构,使用二级指针* @param pos 待插入位置数据* @param data 待插入数据* @return 创建成功返回0,否则返回-1*/
int llist_insert(NODE **head, DATA pos, DATA data)
{// 创建一个新节点// NODE *pNew = __node_create(head,data);NODE *pNew = (NODE*)malloc(sizeof(NODE));// 校验是否创建成功if (!pNew) return -1;// 初始化节点pNew->data = data;pNew->next = NULL;// 因为需要遍历链表得到前后两个位置,所以可以使用指针尾随法NODE *p = *head, *q = NULL;// 情景1:空链表if (!p){*head = pNew; // 更新头指针return 0;}// 情景2:pos是头节点if (memcmp(&(p->data), &pos, sizeof(DATA)) == 0){// 头插法pNew->next = *head;*head = pNew; // 更新头指针return 0;}// 情景3:pos是非头节点while (p){// 校验是否是posif (memcmp(&(p->data), &pos, sizeof(DATA)) == 0){// 插入到目标节点pos前面pNew->next = p; // p就是pos对应的节点q->next = pNew; // q就是pos的上一个节点return 0;}q = p;p = p->next;}// 情景4:找不到pos的位置(使用尾插法,大家也可以选择不插入)q->next = pNew;return 0;
}/*** @brief 遍历链表数据* @param head 待遍历的链表** @return*/
void llist_showAll(const NODE *head)
{// 首先使用一个指针变量接受形参const NODE *p = head;while (p){printf("%d\t", p->data);p = p->next; // 类似与顺序表中的p++,实现相邻节点之间的指针偏移}printf("\n");
}/*** @brief 根据data返回其对应的节点* @param head 待查找的链表* @param data 待查找的节点数据(相同数据只匹配第一个)* * @return 成功返回节点指针,失败返回NULL*/
NODE *llist_find(const NODE *head, DATA data)
{const NODE *p = head;while(p){if (memcmp(&(p->data), &data, sizeof(DATA)) == 0){return (NODE*)p;}p = p->next;}return NULL;
}/*** @brief 更新链表节点数据old为newdata* @param head 待更新的链表* @param old 待更新节点的数据* @param newdata 节点需要更新的数据* * @return 修改成功返回0,否则返回-1*/
int llist_update(const NODE *head, DATA old, DATA newdata)
{NODE *p = NULL;// 找到old对应的NODEif (!(p = llist_find(head,old))){return -1;}// 更新数据p->data = newdata;return 0;
}/*** @brief 删除链表中节点值为data的节点* @param head 待删除节点链表* @param data 待删除节点数据* * @return 删除成功返回0,否则返回-1*/
int llist_delete(NODE **head, DATA data)
{NODE *p = *head, *q = NULL;// 情景1:空链表if (!p) return -1;// 情景2:头节点if (memcmp(&(p->data), &data, sizeof(DATA)) == 0){*head = p->next;// 回收节点free(p);return 0;}// 情景3:非头节点(含尾节点)while(p){if (memcmp(&(p->data), &data, sizeof(DATA)) == 0){q->next = p->next;// 让p的上一个节点的next指向p的下一个节点,实际上就是从链表中将p剔除free(p);return 0;}// 指针偏移q = p;p = p->next;}return -1;
}/*** @brief 销毁链表* @param head 待销毁的链表* * @return */
void llist_destroy(NODE **head)
{NODE *p = *head, *q = NULL;while (p){q = p;p = p->next;free(q);}// 重置头指针*head = NULL;
}

文件:linkedList.h

#ifndef LINKEDLIST_H
#define LINKEDLIST_H// 引用相关头文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 给数据设置一个别名,本次涉及的所有数据结构的存储数据的类型都是int,大家做项目的时候,可以替换成相应的结构体
typedef int DATA;// 创建链表的节点结构体
typedef struct node
{DATA         data;     // 数据域:存放节点数据struct node *next;     // 指针域:指向下一个同类型节点
} NODE;/*** @brief 链表的创建(没有指定头结点的前提下进行(本案例涉及的是无头节点))* @param head 待操作链表,如果是改变链表结构,使用二级指针* @param data 待插入数据* @return 创建成功返回0,否则返回-1*/
extern int llist_create(NODE **head, DATA data);  /*** @brief 向链表头部插入一个节点数据(头插法)* @param head 待操作链表,如果是改变链表结构,使用二级指针* @param data 待插入数据* @return 创建成功返回0,否则返回-1*/
extern int llist_addHead(NODE **head, DATA data);/*** @brief 向链表尾部插入一个节点数据(尾插法)* @param head 待操作链表,如果是改变链表结构,使用二级指针* @param data 待插入数据* @return 创建成功返回0,否则返回-1*/
extern int llist_addTail(NODE **head, DATA data);/*** @brief 向链表指定位置插入一个节点数据(中间插法)* @param head 待操作链表,如果是改变链表结构,使用二级指针* @param pos 待插入位置数据* @param data 待插入数据* @return 创建成功返回0,否则返回-1*/
extern int llist_insert(NODE **head, DATA pos, DATA data);/*** @brief 遍历链表数据* @param head 待遍历的链表* * @return */
extern void llist_showAll(const NODE *head);/*** @brief 根据data返回其对应的节点* @param head 待查找的链表* @param data 待查找的节点数据(相同数据只匹配第一个)* * @return 成功返回节点指针,失败返回NULL*/
extern NODE *llist_find(const NODE *head, DATA data);/*** @brief 更新链表节点数据old为newdata* @param head 待更新的链表* @param old 待更新节点的数据* @param newdata 节点需要更新的数据* * @return 修改成功返回0,否则返回-1*/
extern int llist_update(const NODE *head, DATA old, DATA newdata);/*** @brief 删除链表中节点值为data的节点* @param head 待删除节点链表* @param data 待删除节点数据* * @return 删除成功返回0,否则返回-1*/
extern int llist_delete(NODE **head, DATA data);/*** @brief 销毁链表* @param head 待销毁的链表* * @return */
extern void llist_destroy(NODE **head);#endif
http://www.dtcms.com/a/320371.html

相关文章:

  • 城市道路场景下漏检率↓76%:陌讯多模态融合算法在井盖缺失识别中的实践
  • Nestjs框架: 管道机制(Pipe)从校验到转换的全流程解析
  • ROS Launch 文件中的替换参数详解
  • 1.电动汽车动力电池系统技术介绍与分类
  • 在线文档自动化工具有什么
  • 周志华院士西瓜书实战(三)聚类+邻居+PCA+特征选择+半监督学习
  • 【Canvas与徽章】北极星蓝盘玻璃光徽章
  • NumPy库向量的常见运算
  • C++面试9——多继承陷阱与适用场景
  • 【新闻资讯】Anthropic CEO 达里奥·阿莫迪万字访谈:在技术加速与风险防控间的坚守
  • vLLM:彻底改变大型语言模型推理延迟和吞吐量
  • RabbitMQ面试精讲 Day 14:Federation插件与数据同步
  • YOLOv8面试知识
  • Linux系统编程--基础开发工具
  • 容器之王--Docker的部署及基本操作演练
  • 前端学习 7:EDA 工具
  • Springboot 使用 JPA 分页查询
  • 前端开发工具大全
  • 车辆特征与车牌识别准确率↑29%:陌讯多模态融合算法实战解析
  • 知识蒸馏 - 基于KL散度的知识蒸馏 KL散度的方向
  • 适配器模式及优化
  • 在NVIDIA Orin上用TensorRT对YOLO12进行多路加速并行推理时内存泄漏 (中)
  • linux系统编程
  • 使用winsw把SpringBoot项目注册成window服务
  • javaweb开发之会话_过滤器_监听器
  • 【感知机】感知机(perceptron)学习算法的收敛性
  • 【Unity3D实例-功能-镜头】第三人称视觉-镜头优化
  • 基于深度学习的污水新冠RNA测序数据分析系统
  • Linux机器可直接使用的自动化编译文件
  • AGV_ads通讯exe的创建