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.pswp.cn/news/917982.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/917982.shtml

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

相关文章

使用 decimal 包解决 go float 浮点数运算失真

文章目录问题解决注意问题 go float 在运算的时候会出现精度问题 package mainimport ("fmt" )func main() {var a float64 0.3var b float64 0.6fmt.Println("ab", ab) // 你以为是 0.9 但是结果是&#xff1a;0.8999999999999999 }你观察到的 0.3 …

MongoDB学习专题(六)复制集和分片集群

1、概念MongoDB复制集的主要意义在于实现服务高可用&#xff0c;类似于Redis中的哨兵模式2、功能1. 数据写入主节点时将数据复制到另一个副本节点上2. 主节点发生故障时自动选举出一个新的替代节点在实现高可用的同时&#xff0c;复制集实现了其他几个作用数据分发&#xff1a;…

vue3对比vue2的性能优化和提升 :Vue 3 vs Vue 2

1.性能提升 1.1.响应式系统的改进: 从 Object.defineProperty 到 Proxy Vue2:Vue 2 的响应式系统基于 Object.defineProperty,它为每个属性单独设置 getter 和 setter。虽然能够满足基本需求,但它在以下方面存在性能瓶颈: Vue2 中数组监听的局限性:Vue2 通过Object.defi…

进程生命周期管理:从创建到终止的完整逻辑

前言 在操作系统的世界里&#xff0c;进程就像一个个忙碌的 “工作单元”&#xff0c;从被创建到完成任务后终止&#xff0c;始终遵循着一套严谨的生命周期规则。理解进程的生命周期管理&#xff0c;是揭开操作系统多任务调度神秘面纱的关键 —— 而这其中&#xff0c;进程的创…

【显示器】背光板的结构和工作原理

背光板是LCD&#xff08;液晶显示器&#xff09;中的一个重要组件&#xff0c;它负责提供屏幕所需的光源。下面我们详细解释背光板的结构和工作原理。背光板的基本结构一个典型的背光板由以下几个主要部分组成&#xff1a;LED灯条&#xff1a;通常使用白色LED作为光源。导光板&…

hadoop HDFS 重置详细步骤

有时候我们需要对hdfs重置&#xff0c;步骤如下&#xff1a; 1、停止服务 2. 清除日志节点ssh dmp-hdfs-ns1 rm -rf /disk1/dfs/jn/meta/*ssh dmp-hdfs-ns2 rm -rf /disk1/dfs/jn/meta/*ssh dmp-hdfs-dt1 rm -rf /disk1/dfs/jn/meta/*ssh dmp-hdfs-dt2 rm -rf /disk1/dfs/jn/me…

前端性能优化:从请求到资源的精细调控

在用户体验为王的时代&#xff0c;前端性能直接决定产品的留存率。本文聚焦 “减少不必要的传输与加载损耗”&#xff0c;从 合并HTTP请求、启用压缩、减少Cookie、资源加载顺序 四个维度&#xff0c;拆解优化思路与落地方法。 一、合并HTTP请求&#xff1a;突破浏览器并发瓶颈…

【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 微博舆情数据可视化分析-热词情感趋势柱状图

大家好&#xff0c;我是java1234_小锋老师&#xff0c;最近写了一套【NLP舆情分析】基于python微博舆情分析可视化系统(flaskpandasecharts)视频教程&#xff0c;持续更新中&#xff0c;计划月底更新完&#xff0c;感谢支持。今天讲解微博舆情数据可视化分析-热词情感趋势柱状图…

脚本统计MongoDB集合结构信息

场景&#xff1a; 当想统计mongodb集合的结构是什么数据类型时。 1.利用variety.js解析 https://github.com/variety/variety 2.脚本 #!/bin/bash#userxxx #passwxxx host1xx.1x.1x.150 port27010 dbhgrtabs$(echo "show collections"|mongo ${host}:${port}/${db}|g…

订单簿流动性分析与机器学习在大单匹配中的应用

一、订单簿流动性的基本概念 1.1 订单簿的结构与组成 在金融市场中&#xff0c;订单簿&#xff08;Order Book&#xff09;是买卖双方提交的限价订单的集合&#xff0c;通常以价格优先、时间优先的原则进行排序。订单簿由多个层级的价格档位组成&#xff0c;每个档位包含若干限…

CSS :is () 与 :where ():简化复杂选择器的 “语法糖”

在 CSS 编写中&#xff0c;你是否遇到过这样的场景&#xff1a;需要给多个不同父元素下的子元素设置相同样式&#xff0c;结果写出一长串重复的选择器&#xff1f;比如给header、main、footer中的p标签设置相同的颜色&#xff0c;传统写法可能是header p, main p, footer p { c…

vue打包号的文件如何快速查找文件打包后的位置

解析“explorer yz-front-dist”&#xff1a;前端开发者的实用命令小知识 在前端开发的日常工作中&#xff0c;我们经常会接触到各种命令行操作&#xff0c;其中“explorer yz-front-dist”是一个看似简单却暗藏实用价值的命令。对于刚接触开发的新手来说&#xff0c;理解它的含…

Go语言数据类型深度解析:位、字节与进制

Go语言数据类型深度解析&#xff1a;位、字节与进制 在计算机编程中&#xff0c;数据类型是构建一切的基础。理解不同数据类型的特性、内存占用以及在不同场景下的应用&#xff0c;对于编写高效、可靠的代码至关重要。 本文将深入探讨Go语言中的数据类型系统&#xff0c;重点讲…

计算机视觉(opencv)——图像本质、数字矩阵、RGB + 基本操作(实战一)

OpenCV 入门教程&#xff1a; OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个开源的计算机视觉库&#xff0c;广泛应用于图像处理、视频分析、机器学习等领域。 在 Python 中&#xff0c;cv2 是 OpenCV 的主要接口模块。本文将带你一步步掌握 cv2…

【数据库】使用Sql Server创建索引优化查询速度,一般2万多数据后,通过非索引时间字段排序查询出现超时情况

大家好&#xff0c;我是全栈小5&#xff0c;欢迎来到《小5讲堂》。 这是《Sql Server》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录前言SQL 创建索引…

MyBatis联合查询

文章目录数据库设计MyBatis 配置MyBatis 映射文件Mapper 接口总结数据库设计 建表 SQL CREATE TABLE user (id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(50) NOT NULL );CREATE TABLE order (id INT PRIMARY KEY AUTO_INCREMENT,user_id INT NOT NULL,order_no VARCHAR(…

项目中使用的设计模式

项目中使用的设计模式请列举几个项目中常用的设计模式什么是设计模式&#xff0c;在项目中使用了那些设计模式动态代理模式JDK动态代理CGLIB动态代理单例模式懒汉式&#xff08;非线程安全&#xff09;饿汉式懒汉式&#xff08;线程安全&#xff09;工厂模式观察者模式装饰器模…

实战教程:从“对象文件为空“到仓库重生——修复 Git 仓库损坏全记录

文章目录实战教程&#xff1a;从"对象文件为空"到仓库重生——修复 Git 仓库损坏全记录案发现场&#xff1a;一个严重损坏的仓库修复之旅&#xff1a;四步让仓库重获新生准备工作&#xff1a;创建安全备份第 1 步&#xff1a;清理战场——删除所有空对象第 2 步&…

ansible 操作家族(ansible_os_family)信息

1. 操作系统系列 &#xff08;ansible_os_family&#xff09;ansible web -m setup -a filteransible_os_family2. 操作系统家族为 RedHat 时执行任务--- - hosts: websrvsremote_user: roottasks:- name: Install package on RedHat systemsyum:name: httpdstate: presentwhen…

一文学会c++继承 组合

文章目录继承简介定义访问限定符和继承方式⭐基类派生类赋值转换继承的作用域派生类的默认成员函数继承与友元继承与静态成员⭐复杂的菱形继承虚拟继承组合继承简介 继承是面向对象程序设计代码复用的重要手段&#xff0c;使得程序员可以在保持原类的基础上扩展&#xff0c;新…