C语言数据结构——链表

news2025/1/12 6:09:20

C语言数据结构——链表

链表包括单链表,双链表,循环链表等。

而今天要说的是单链表,它是一个线性表,它在内存中是无序的,由一个个指针来连接。

图示:

在这里插入图片描述

小方块代表的就是存储的数据,箭头就是指向下一个数据存储地的指针,有了这个,数据才可以串联在一起。

一、实现链表

实现链表这个数据本身还是不难的,最重要的是实现各个方法,如排序,增删改查操作等。

C语言实现链表:

/**
 * @brief 手写单链表(有头节点)
 * 
 */
#include <stdio.h>
typedef int bool;//自定义布尔变量,因为C语言没有布尔这个数据类型
#define true 1//使用数字1来表示true,实际上在别的语言中1就是true
#define false 0//同理
typedef struct{
    int value;//当前节点存储的值
    LinckList* next;//指向下一节点的指针
    //int length;//链表长度,总共有多少节点
    //这其实算是偷懒了,不带上也可以遍历单链表来记录长度
    //弊端每个结构体是多占了一个int字节的内存,不建议携带
}LinckList;

value:代表的就是上面图的小方块,存储这int类型的值

next:指向下一个链表的指针、

二、实现一些功能

作为一种数据结构,我们既然实现了它,那么它的功能我们也实现一遍才好。

其实并不难,不要害怕,我会细说的。

1. 初始化

初始化链表,使指针指向null,使数据初始化为0。

/**
 * @brief 初始化单链表
 * 
 * 对节点赋值值,下一节点的指针指向NULL
 * 
 * @param LinckList 
 * @param data 初始化传入的值
 */
void InitLinck(LinckList* LinckList, int data)
{
    LinckList->value = data;//将传入的参数赋值
    LinckList->next = NULL;//默认只有一个节点
}

还是比较简单的。

2. 增

给一个数据,将它添加到链表中。添加方式有很多种,添加到头部,添加到尾部,添加到指定位置。我会一一讲解。

1. 将数据添加到链表尾部(一般人的思路就是这个)

使用遍历的方法,直接遍历到最后一个,然后创建一个链表数据类型,让最后节点的链表指向新链表,并对新链表赋值。

/**
 * @brief 将指定元素插入最后节点
 * 1. 赋值头节点
 * 2. 遍历链表
 * 3. 遍历完声明新节点
 * 4. 新节点值赋值data
 * 5. 最后节点指向新节点
 * 
 * @param linckList 可以传参指针和引用
 * @param data 
 */
void InsertDataLast(LinckList* linckList, int data)
{
    LinckList* p = linckList;//把头链表记录一下,不然直接遍历链表的话,最后链表就是遍历完的样子
    while (p->next)//当括号为p时是遍历所有节点,最后p为NULL
    {
        p = p->next;
    }
    LinckList root;
    root.value = data;
    p->next = &root;
}

2. 将数据添加到链表头部

  1. 声明新节点
  2. 让新节点的值为头节点的
  3. 让新节点的指向针为头节点的
  4. 头节点的值为data,指向新节点
/**
 * @brief 将数据插入链表的头部
 * 1. 声明新节点
 * 2. 让新节点的值为头节点的
 * 3. 让新节点的指向针为头节点的
 * 4. 头节点的值为data,指向新节点
 * 
 * @param linckList 可以传参指针和引用
 * @param data 
 */
void InsertDataFirst(LinckList* linckList, int data)
{
    LinckList p;
    p.value = linckList->value;
    p.next = linckList->next;
    linckList->next = &p;
    linckList->value = data;
}

3. 将数据添加到指定位置

  1. 找到那个节点
  2. 新建一个结构体
  3. 将data值传入结构体
  4. 将第index个节点的next的值指向结构体
  5. 将结构体的next的值指向原链表第index+1个节点
/**
 * @brief 向链表中的指定节点插入指定元素
 * 
 * 大致的思路就是:
 * 1. 找到那个节点
 * 2. 新建一个结构体
 * 3. 将data值传入结构体
 * 4. 将第index个节点的next的值指向结构体
 * 5. 将结构体的next的值指向原链表第index+1个节点
 * 
 * @param linckList 
 * @param data 
 */
LinckList* InsertData(LinckList* linckList, int index, int data)
{
    LinckList* p = linckList;//保存主节点,用于返回
    LinckList linck;//新建单链表节点
    InitLinck(&linck, data);//初始化此单链表节点,将data传入
    // linck.value = data;效果上同
    for (int i = 1; i < index; i++)
    {
        linckList = linckList->next;//将主节点遍历到第index-1个
    }
    linck.next = linckList->next;//将结构体指向节点的第index个节点
    linckList->next = &linck;//将第index-1个节点
    return p;
}

3. 删

删除链表中的元素,就不写删除头部和尾部的了,直接写删除指定元素

  1. 遍历链表
  2. 遍历到指定元素的上一个节点,将节点指向下下一节点即可
/**
 * @brief 删除指定元素
 * 1. 遍历链表
 * 2. 遍历到指定元素的上一个节点,将节点指向下下一节点即可
 * 
 * @param linckList 
 * @param data 
 */
void DeleteData(LinckList* linckList, int data)
{
    if (linckList->value == data)
    {
        //如果第一个就是,那就直接吧第一个删了
        linckList->value = linckList->next->value;
        linckList = linckList->next;
    }
    LinckList* p = linckList;
    for (int i = 1; i < GetLength(linckList); i++)
    {
        if (linckList->next->value == data)
        {
            linckList->next = linckList->next->next;
            break;
        }
        linckList = linckList->next;
    }
}

哦对了,还有删除整个链表的。

和初始化差不多,将指针指向null,数据改掉就行了。

/**
 * @brief 销毁链表
 * 
 * 和初始化的方法差不多
 * 
 * @param LinckList 
 */
void DestoryLinck(LinckList* LinckList)
{
    LinckList->value = 0;//存储值置0
    LinckList->next = NULL;//下一节点指向NULL
}

4. 改

修改链表中的值。

方法:遍历+判断

/**
 * @brief 修改某个元素为另一个元素
 * 遍历+判断
 * 
 * @param linckList 
 * @param oldData 
 * @param newData 
 */
LinckList* UpdataData(LinckList* linckList, int oldData, int newData)
{
    LinckList* p = linckList;
    while (p)
    {
        if (p->value == oldData)
        {
            p->value = newData;
            break;
        }
    }
    return p;
}

5. 查

查找某个元素,感觉作用不大,所以这个就写成将整个链表输出成一个数组吧。

方法:

  1. 遍历输出
  2. 因为不需要修改数组,所以直接传linckList
/**
 * @brief 输出整个链表存储的值
 * 1. 遍历输出
 * 2. 因为不需要修改数组,所以直接传linckList
 * @param linckList 
 */
void PrintLinckList(LinckList linckList)
{
    LinckList*p = &linckList;
    printf("[");
    while (p->next != NULL)//遍历到最后一个是因为要把最后一个的 ”, “去掉
    {
        printf("%d, ", linckList.value);
        p = p->next;
    }
    printf("%d", p->value);
    printf("]\n");
}

输出的形式就是像:[1, 2, 3, 4]这样的

6. 获取长度

这个实现起来也很简单,遍历链表,使用变量记录遍历次数即可。

  1. 赋值原来的头节点
  2. 定义一个int类型的变量表示长度
  3. 遍历链表,长度每次循环+1
  4. 返回长度
/**
 * @brief 获取链表的长度
 * 
 * 1. 赋值原来的头节点
 * 2. 定义一个int类型的变量表示长度
 * 3. 遍历链表,长度每次循环+1
 * 4. 返回长度
 * 
 * @param linckList 
 * @return length 数组长度
 */
int GetLength(LinckList* linckList)
{
    int length = 0;
    LinckList* p = linckList;
    while (p)
    {
        p = p->next;
        length++;
    }
    return length;
}

结语

积少成多,聚沙成塔,每天走一点,在长的路也能走完。

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

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

相关文章

(46)STM32——FATFS文件系统实验

目录 学习目标 运行结果 文件系统 常用系统 FATFS 特点 结构图 移植步骤 disk_initialize disk_status disk_read disk_write disk_ioctl get_fattime 代码 总结 学习目标 我们要来介绍的是FATFS文件系统&#xff0c;这是一个为嵌入式设计的文件系统&#xff0c…

大学科目网课搜题接口

大学科目网课搜题接口 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 查题校园题库&#xff1a;查题校园题库后台&#xff08;点…

【1024社区大奖】让你一小时内狂揽大奖[保姆级教程①]

四层挑战&#xff0c;一小时内拿捏&#xff01;&#xff08;上&#xff09;一、龙蜥社区大奖二、战前准备1.注册码云Gitee2.注册龙蜥社区三、开始挑战&#xff0c;包揽大奖&#xff01;①第一层&#xff1a;小龙推荐 [15分钟]②第二层随机试炼 [15分钟]一、龙蜥社区大奖 活动分…

【论文笔记】Transformer-based deep imitation learning for dual-arm robot manipulation

【论文笔记】Transformer-based deep imitation learning for dual-arm robot manipulation Abstract 问题&#xff1a;In a dual-arm manipulation setup, the increased number of state dimensions caused by the additional robot manipulators causes distractions and …

微信小程序入门与实战之更多电影列表与电影搜索

wx.request的更多参数详解 在网络请求中我们可以采用下面这种形式&#xff1a; 如果我们可以不采用直接写在url的方式我们可以采用data的方式&#xff1a; 默认请求方式是GET我们可以通过设置method修改请求方式&#xff1a; 更多电影页面 我们要实现的效果&#xff1a;…

应用层——HTTP协议

文章目录一、应用层1.1 应用层概念1.2 再谈协议二、网络版本的计算器网络计算器编码部分版本1&#xff1a;原生版本版本2&#xff1a;引入序列化和反序列化三、HTTP协议3.1 URL3.2 urlencode和urldecode3.3 HTTP协议格式3.3.1 请求报文3.3.2 响应报文3.4 HTTPDemo3.4.1改进3.4.…

这里不适合做技术

6点&#xff0c;の&#xff0c;下班了又是一个差不多一样的星期过去了&#xff0c;又是一个差不多的周末要到来了。我也差不多要离开这家公司了&#xff0c;入职4年多&#xff0c;那时候雄心壮志&#xff0c;决定干一番大事业&#xff0c;那个时候的自己&#xff0c;技术的炉火…

【图像融合】基于 DCT结合拉普拉斯金字塔的图像融合附matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

论文理解与笔记【CVPR_2022.6】Region-Aware Face Swapping

论文理解与笔记【CVPR_2022】Region-Aware Face Swapping论文的缩写全拼&#xff1a;一、贡献二、介绍三、提出问题&#xff0c;也是论文解决的问题四、具体实现方案五、实验六、最终感想和总结论文地址&#xff1a;传送门或者传送门2 先看看效果&#xff1a; 论文的缩写全拼…

【单片机毕业设计】【mcuclub-jj-003】基于单片机的八层电梯的设计

最近设计了一个项目基于单片机的八层电梯系统&#xff0c;与大家分享一下&#xff1a; 一、基本介绍 项目名&#xff1a;八层电梯 项目编号&#xff1a;mcuclub-jj-003 单片机类型&#xff1a;STC89C52、STM32F103C8T6 功能简介&#xff1a; 1、通过3*4矩阵键盘实现电梯内部…

【Linux修炼】5.vim详解

每一个不曾起舞的日子&#xff0c;都是对生命的辜负。 Linux-yum&vim工具的使用本节目标1. Linux 软件包管理器 yum1.1 什么是软件包1.2 关于rzsz1.3 查看软件包1.4 如何安装软件1.5 如何卸载软件2. Linux编辑器-vim的使用&#xff08;重点&#xff09;2.1 什么是vim&#…

C++内存管理

1.C内存分布 学习C内存分布之前&#xff0c;先小试牛刀一下。 int globalVar 1; static int staticGlobalVar 1; void Test() {static int staticVar 1;int localVar 1;int num1[10] { 1, 2, 3, 4 };char char2[] "abcd";const char* pChar3 "abcd&quo…

webpack--》webpack底层深入讲解,从初识到精通,真正实现从0到1的过程

目录 webpack webpack的基本使用 安装 配置 修改自定义打包的入口与出口 优化js或图片的存放路径 配置webpack中符号的使用 webpack中相关插件安装 webpack-dev-server html-webpack-plugin clean-webpack-plugin webpack中的loader 打包处理css文件 打包处理les…

TPM分析笔记(十二)TPM PCR操作

目录一、PCR初始化&#xff08;Initializing PCR&#xff09;二、PCR的扩展&#xff08;Extend of a PCR&#xff09;2.1 其他PCR命令三、使用PCR Banks进行扩展&#xff08;Using Extend with PCR Banks&#xff09;四、事件记录&#xff08;Recording Events&#xff09;五、…

Java递归实现树形结构的两种方式

目录0、引言1、数据准备2、类型转化3、递归实现方法3.1、Java7及以下纯Java递归实现3.2、Java8及以上借助lamda表达式实现0、引言 在开发的过程中&#xff0c;很多业务场景需要一个树形结构的结果集进行前端展示&#xff0c;也可以理解为是一个无限父子结构&#xff0c;常见的…

【老师见打系列】:我只是写了一个自动回复讨论的脚本~

文章目录&#x1f31f;好久不见⛳️实现过程&#x1f334;老操作了兄弟们~&#x1f422;一步拿捏讨论&#x1f496;美图结束语专栏Python零基础入门篇&#x1f4a5;Python网络蜘蛛&#x1f4a5;Python数据分析Django基础入门宝典&#x1f4a5;小玩意儿&#x1f4a5;Web前端学习…

2022海德堡桂冠论坛(HLF)见闻录

今年9月下旬&#xff0c;我前往德国参加了第九届海德堡桂冠论坛。因疫情原因停摆两年后&#xff0c;海德堡桂冠论坛再次以线下形式举办&#xff0c;会场热闹非凡&#xff0c;作为计算机与数学界的社交盛宴当之无愧。 海德堡桂冠论坛&#xff08;Heidelberg Laureate Forum, HLF…

IGV-GSAman |「功能基因组时代」的高效率科研工具

写在前面 今天周末&#xff0c;转眼10月份只剩一周。万万没想到&#xff0c;一个月下去&#xff0c;我还是花了不少时间在完善「GSAman」。至于为什么本来「两个小时」就干完的事情&#xff0c;可以干成「22天」&#xff1f;到底还是我对前面的版本&#xff0c;不太满意。当然…

Attack Lab

Attack Lab 从CMU官网下载完所需实验包后&#xff0c;内有官方文档以及.tar压缩包&#xff0c;使用tar -xvf targetk.tar解压后&#xff0c;得到如下文件 The fifiles in targetk include: README.txt: A fifile describing the contents of the directory ctarget: An execut…

web自动化测试框架

本文介绍web自动化测试框架 ●Base&#xff1a;用来对Selenium API进行二次封装。 对Selenium API进行二次封装的目的是简化一些复杂的操作&#xff0c;但是千万不要为了封装而封装。 封装好后&#xff0c;其他页面类可以集成basepage&#xff0c;调用这些方法。 from sele…