当前位置: 首页 > 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.lryc.cn/news/613664.html

相关文章:

  • 北京-4年功能测试2年空窗-报培训班学测开-第七十天-面试第一天
  • rebase 和pull的通俗区别是什么
  • Flink与Kafka核心源码详解-目录
  • 【Unity3D实例-功能-镜头】第三人称视觉-镜头优化
  • 秋招笔记-8.7
  • iSCSI 服务器
  • 《C语言》函数练习题--3
  • 5分钟了解OpenCV
  • 【MATLAB】(十)符号运算
  • XCZU19EG-2FFVB1517I FPGA Xilinx AMD ZynqUltraScale+ MPSoC
  • 《C语言》指针练习题--1
  • Gitee上免费搭建博客
  • 从“炼金术”到“工程学”:深度学习十年范式变迁与未来十年路线图
  • UnivNet论文分析(20210615)
  • 为何毫米波需要采用不同的DPD方法?如何量化其值?
  • 机器学习之随机森林(Random Forest)实战案例
  • OpenAI 开源模型 GPT-OSS深度拆解:从1170亿参数到单卡部署,重构AI开源生态
  • Java面试宝典:类加载
  • 敏捷总结-上
  • 智能制造的中枢神经工控机在自动化产线中的关键角色
  • C++的入门学习
  • TCP粘包问题详解与解决方案
  • 如何在 Ubuntu 24.04 中永久更改主机名
  • MySQL面试题及详细答案 155道(061-080)
  • 动手学深度学习(pytorch版):第一章节——引言
  • DataEase官方出品丨SQLBot:基于大模型和RAG的智能问数系统
  • MCU-TC397的UCB初识
  • Effective C++ 条款27: 尽量用const、enum、inline替换 #define
  • 通过CNN、LSTM、CNN-LSTM及SSA-CNN-LSTM模型对数据进行预测,并进行全面的性能对比与可视化分析
  • JavaEE 初阶第十五期:文件 IO 的 “管道艺术”(上)