【知识分享】数据结构的应用——链表

2023-11-11

背景

    对于数据结构,其实学过C语言的都不陌生,无外乎就队列、栈、二叉树等等这些。但其实对于初学者最困惑的不是数据结构是怎么样的,而是数据结构有什么用?我自己也是工作好几年后才体验到数据结构的快乐。所以本系列文章重点从应用场景切入,让大家理解数据结构的好处。

简介

    链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。由于在物理存储上可以以非连续状态存在,使得链表变得极其灵活。同时,链表就是C语言指针最完美的表现形式之一。常见的链表形式有两种,单向链表和双向链表。

  • 单向链表

    一个节点数据带一个链接信息,如下图所示,链路方向只有一个,只能一直往后链,查找方式也只能从头到尾地遍历,无法反向查找。
链表1

  • 双向链表

    一个节点数据带两个链接信息,一个用于链接下个节点,一个用于链接上个节点,这种结构使得可以从任意一个节点找到另个一个节点的数据。
链表2
    单向链表和双向链表,其实这只是链表在顺序队列中结合的体现,实际链表的应用远不止这两种,它可以跟任意一种数据结构结合,比如树,二叉树的应用很大程度依赖于链表的实现。还有一些散列表,也可以由链表实现。

应用场景

    链表应用面极为广泛,上面已经教你学会1+1=2,接下来要开始来做微积分了。举个GUI设计的场景,假设现在让你做一个显示屏,用户可通过拉取按键组件自己编辑界面内容,而你要做的,就是把用户编辑的内容显示出来,那应该怎么做?我们先用普通方式来实现。

#include <stdint.h>

/* 定义按键创建的最大个数 */
#define BUTTON_MAX_NUM			100

/* 按键的内置信息 */
struct tagButtonPrv
{
	int32_t StartX;		/* 起始横坐标 */
	int32_t StartY;		/* 起始纵坐标 */
	int32_t EndX;		/* 结束横坐标 */
	int32_t EndY;		/* 结束纵坐标 */
	…………
};

/* 首先得有个按键的结构对象 */
struct tagButtonCB
{
	struct tagButtonPrv Member[BUTTON_MAX_NUM];
	uin32_t Num;
};

struct tagButtonCB Button;

/* 用户创建按键信息 */
void CreateButton(struct tagButtonPrv data)
{
	Button.Member[Button.Num].StartX = data.StartX;
	Button.Member[Button.Num].StartY = data.StartY;
	Button.Member[Button.Num].EndX = data.EndX;
	Button.Member[Button.Num].EndY = data.EndY;
	…………
	
	Button.Num++;
}

/* 根据按键信息显示按键 */
void DisplayButton(struct tagButtonPrv* data)
{
	/* 显示的相关操作,这里就不展示了 */
}

/* 用户操作任务 */
void UserTask(void)
{
	struct tagButtonPrv data;
	data.StartX = 0;
	data.StartY = 0;
	data.EndX = 100;
	data.EndY = 100;
	…………

	CreateButton(data);
	
	while(1)
	{
		/* 干其他事 */
	}
}

/* 显示任务 */
void DisplayTask(void)
{
	while(1)
	{
		/* 把用户加的按键显示出来 */
		for (uint32_t i = 0; i < Button.Num; i++)
		{
			DisplayButton(&Button.Member[i]);
		}
	}
}

int main(void)
{
	/* 创建用户操作和显示刷新两个任务--伪代码,重在逻辑理解 */
	task_create(UserTask);
	task_create(DisplayTask);
	return 0;
}

    上面这种实现方式有个很明显的问题,就是按键数量的上限是固定的,如果此时用户想要编写一个庞大的工程时,100个按键可能完全不够用。所以这里动态分配空间的方式来实现,再加上一个指针的链接信息,就可以组成一个链表。

#include <stdint.h>

/* 按键的内置信息 */
struct tagButtonPrv
{
	int32_t StartX;		/* 起始横坐标 */
	int32_t StartY;		/* 起始纵坐标 */
	int32_t EndX;		/* 结束横坐标 */
	int32_t EndY;		/* 结束纵坐标 */
	…………
};

struct tagButton
{
	struct tagButtonPrv Member;
	struct tagButton *Next;			/* 链接信息 */
};

struct tagButton *ButtonHead = NULL;

/* 用户创建按键信息 */
void CreateButton(struct tagButton *data)
{
	struct tagButton *i = ButtonHead;
	
	/* 查找到链表的尾部,把信息添加进去 */
	for (; i != NULL; i = i->Next);
	i = data;
}

/* 根据按键信息显示按键 */
void DisplayButton(struct tagButton* data)
{
	/* 显示的相关操作,这里就不展示了 */
}

/* 用户操作任务 */
void UserTask(void)
{
	while(1)
	{
		/* 用户配置了按键后执行动作 */
		if (xxx)
		{
			struct tagButtonPrv *data = malloc(sizeof(struct tagButtonPrv));
	
			data->StartX = 0;
			data->StartY = 0;
			data->EndX = 100;
			data->EndY = 100;
			…………
		
			CreateButton(data);
		}
		/* 干其他事 */
	}
}

/* 显示任务 */
void DisplayTask(void)
{
	while(1)
	{
		/* 把用户加的按键显示出来 */
		for (struct tagButton *i = ButtonHead; i != NULL; i = i->Next)
		{
			DisplayButton(i);
		}
	}
}

int main(void)
{
	/* 创建用户操作和显示刷新两个任务--伪代码,重在逻辑理解 */
	task_create(UserTask);
	task_create(DisplayTask);
	return 0;
}

    那再如果,现在要显示的不止是按键,还有文本框、图片等组件,此时作为不同组件封装的结构和所占空间大小是不一样的,如果要把它们都链接到同一个链里,以上面这种链表包含数据信息的写法是无法满足的,这里我们再来提一种实现方式,也是Linux操作系统里的经典链表应用,在数据信息里添加单独的链信息。
链表3

#include <stdint.h>

/* 链接信息单独封装 */
struct tagLink
{
	struct tagLink *Next;
};

/* 文本框的内置信息 */
struct tagTextPrv
{
	struct tagLink *Next;	/* 数据中插入链信息,要放在数据前面,方便后面操作 */
	
	int32_t StartX;		/* 起始横坐标 */
	int32_t StartY;		/* 起始纵坐标 */
	int32_t EndX;		/* 结束横坐标 */
	int32_t EndY;		/* 结束纵坐标 */
	int8_t *Str;		/* 显示的文本内容 */
	…………
};

/* 按键的内置信息 */
struct tagButtonPrv
{
	struct tagLink *Next;	/* 数据中插入链信息,要放在数据前面,方便后面操作 */
	
	int32_t StartX;		/* 起始横坐标 */
	int32_t StartY;		/* 起始纵坐标 */
	int32_t EndX;		/* 结束横坐标 */
	int32_t EndY;		/* 结束纵坐标 */
	…………
};

/* 定义一个链头 */
struct tagLink *LinkHead = NULL;

/* 用户创建组件信息 */
void CreateComponent(void *data)
{
	struct tagLink *i = LinkHead;
	
	/* 查找到链表的尾部,把信息添加进去 */
	for (; i != NULL; i = i->Next);
	i = (struct tagLink *)data;
}

/* 根据组件信息显示组件 */
void DisplayComponent(void* data)
{
	/* 显示的相关操作,这里就不展示了 */
}

/* 用户操作任务 */
void UserTask(void)
{
	while(1)
	{
		/* 用户配置了按键后执行动作 */
		if (xxx)
		{
			struct tagButtonPrv *data = malloc(sizeof(struct tagButtonPrv));
	
			data->StartX = 0;
			data->StartY = 0;
			data->EndX = 100;
			data->EndY = 100;
			…………
		
			CreateComponent((void *)data);
		}

		/* 用户配置了文本框后执行动作 */
		if (xxx)
		{
			struct tagTextPrv *data = malloc(sizeof(struct tagTextPrv));
	
			data->StartX = 0;
			data->StartY = 0;
			data->EndX = 100;
			data->EndY = 100;
			data->Str = "Hello!!";
			…………
		
			CreateComponent((void *)data);
		}
		/* 干其他事 */
	}
}

/* 显示任务 */
void DisplayTask(void)
{
	while(1)
	{
		/* 把用户加的按键显示出来 */
		for (struct tagLink *i = LinkHead; i != NULL; i = i->Next)
		{
			DisplayComponent((void *)i);
		}
	}
}

int main(void)
{
	/* 创建用户操作和显示刷新两个任务--伪代码,重在逻辑理解 */
	task_create(UserTask);
	task_create(DisplayTask);
	return 0;
}

    这里可能有人会问了,上面两个例子不都是实现功能了么,那两者有什么区别?第一种是链表中包含数据,而第二种是数据里面添加链接信息,所以可以任意给定链表中的数据长度。而且第二种方式,链表是可以单独封装成模块的,具体可以参考Linux内核里链表的实现。

总结

  1. 链表一般由链接信息和数据两部分组成。
  2. 在数据不连续且个数不确定的情况,可使用链表进行链接。
  3. 根据不同的使用情况,链接信息可以有多个。所以链表可以与队列、树、图等其他数据结构结合使用。
  4. 缺点:需要多一些空间用于存储链接信息,所以对于有序连续的数据,就没必要使用链表。

相关链接

队列、栈、树、图、散列表

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【知识分享】数据结构的应用——链表 的相关文章

  • 【数据结构】MaxHeap 大顶堆

    数据结构源码 实现类 import java util Random public class MaxHeap
  • [基础数据结构] 判断是否为完全二叉搜索树

    对二叉搜索树的定义是 一棵深度为k的有n个结点的二叉树 对树中的结点按从上至下 从左到右的顺序进行编号 如果编号为i 1 i n 1 i n 1 i n 的结点与满二叉树中编号为i的结点在二叉树中的位置相同 则这棵二叉树称为
  • 【数据结构】Stack 栈

    数据结构源码 接口 public interface Stack
  • 【数据结构-图】1.图的构造和遍历(基本理论+代码)

    一 图的基本概念 图 图G是一个有序二元组 V E 其中V称为顶集 Vertices Set E称为边集 Edges set E与V不相交 它们亦可写成V G 和E G 其中 顶集的元素被称为顶点 Vertex 边集的元素被称为边 edge
  • 数据结构:Trie字符串统计

    维护一个字符串集合 支持两种操作 I x 向集合中插入一个字符串 x Q x 询问一个字符串在集合中出现了多少次 共有 N 个操作 所有输入的字符串总长度不超过 1e5 字符串仅包含小写英文字母 Trie树 高效存储和查找字符串 按树结构存
  • springboot项目配置定时任务及注解时间配置

    SpringApplication引入注解 EnableScheduling 开启定时任务 在自定义类上加入注解 Component 可以不使用 在 Configuration 中存在 Configuration 在具体的方法上加入注解 S
  • 数据结构——单调栈

    单调栈 定义 单调递增栈 单调递增栈就是从栈底到栈顶数据是从小到大 单调递减栈 单调递减栈就是从栈底到栈顶数据是从大到小 实现 以单调递增栈为例 向栈中推入元素时 如果栈顶元素比当前元素大 则将栈顶元素推出 直到栈顶元素比当前元素小或者栈为
  • 分布式任务调度平台xxl-job

    一 java的集中式任务调度 while true Thread sleep 轮询 线程休眠的方式实现定时任务 java util Timer java util TimerTask Timer是一种定时器工具 用于使用后台线程计划执行指定
  • 数据结构——>栈

    栈 栈的介绍 栈的应用场景 栈的代码实现 实现栈的思路分析 入栈 出栈 遍历栈 栈的介绍 1 栈是一个先入后出的有序列表 想象成弹夹 2 变化的一端为栈顶 固定的一端为栈底 3 入栈演示图 4 出栈演示图 栈的应用场景 1 递归 2 四则运
  • 【知识分享】C语言应用-易错篇

    一 C语言简介 C语言结构简洁 具有高效性和可移植性 因此被广泛应用 但究其历史的标准定义 C语言为了兼容性在使用便利性作出很大牺牲 在 C陷阱与缺陷 一书中 整理出大部分应用过程中容易出错的点 本文为 C陷阱与缺陷 的浓缩版本 想要更详细
  • tcpdump: Couldn‘t find user ‘tcpdump‘问题解决

    直接vi etc passwd 加入以下一行 tcpdump x 72 72 sbin nologin 就可以了
  • 【数据结构】UnionFind 并查集-2

    数据结构源码 UnionFind1 接口 public interface UnionFind int getSize boolean isConnected int p int q void unionElements int p int
  • 【数据结构】LoopQueue 循环队列

    数据结构源码 接口 public interface Queue
  • 达梦数据库,自定义FIND_IN_SET 函数,用于逗号分隔匹配字符串

    CREATE OR REPLACE FUNCTION FIND IN SET piv str1 varchar2 piv str2 varchar2 p sep varchar2 RETURN NUMBER IS l idx number
  • [NC] 仓鼠与珂朵莉-分块

    给定一个长度为n的序列 m个询问 每次给出一个区间 查找区间内x cnt x 的最大值 由于题目的限制 下一次询问的区间会受到上一次查询结果的影响 所以必须要进行强制在线处理 首先将数列分成ceil n blk 1 块 对于询问中b l 1
  • 数据结构:数组模拟栈

    实现一个栈 栈初始为空 支持四种操作 push x 向栈顶插入一个数 x pop 从栈顶弹出一个数 empty 判断栈是否为空 query 查询栈顶元素 用数组模拟栈 栈 先进后出 include
  • [Atcoder ABC222] F - Expensive Expense

    Time Limit 4 sec Memory Limit 1024 MB Score 500 points Problem Statement The Kingdom of AtCoder is composed of N N N tow
  • CSS媒体查询@media and screen指令在部分(360、奇安信等)浏览器不生效的解决方案

    一 介绍出现问题的写法 media screen and width lt 1024px flex other display none 可以看到 乍一看没什么问题 而且在chrome edge等浏览器也生效 但是在360浏览器等其它浏览器
  • 掩码、ip段转为单个ip地址,解决ValueError: IP(‘x.x.x.x/x‘) has invalid prefix length ()

    最近碰到的问题 简单记录下 from IPy import IP import re os time 解析10 245 1 1 10 245 1 10这种类型的ip段 def all for one dates ipx dates spli
  • Win11如何下载安装java?

    一 问题描述 我在复现论文代码的时候 遇到了这样的问题 我没有下载java 那么该如何解决呢 下载 Java 的作用是为了能够在计算机上运行使用 Java 语言编写的应用程序 Java 是一种广泛使用的编程语言 可用于开发各种类型的应用程序

随机推荐

  • 图书管理系统2.0——mysql数据库

    目录 一 简要介绍 1 使用技术 2 简要功能 3 源码 二 需求文档 1 登录 2 注册 3 用户菜单 3 1 借阅图书 3 1 归还图书 3 2 个人中心 3 2 1 查看所有借阅记录 3 2 2 查看借阅中的图书 3 2 3 签到领积
  • 线程基础篇(十五)之使用ReentrantLock实现消费者生产者

    author Dora date 2020 4 8 9 55 public class QueueLearn 使用读写锁 实现队列的消费 实现一个队列 static ConcurrentLinkedQueue queue new Concu
  • 解决eclipse中出现BASE64Encoder cannot be resolved to a type

    在eclipse中 在进行文件下载时控制台出现 BASE64Encoder cannot be resolved to a type情况导致文件无法下载 针对以上的情况可以试试以下方法 第一种 然后重新运行一下项目 看是否成功 如果不可以就
  • 泰勒公式回顾贴

    泰勒公式 sinx 和 arcsinx 第二项符号不同 sinx x 1 6 x 3 arcsinx x 1 6 x 3 sinx 和 cosx的区别 sinx的系数是奇数阶乘 1 3 5 cosx的系数是偶数阶乘 2 4 6 tanx 和
  • 服务器主机本地系统开机,本地主机启动tomcat v9.0服务器错误

    我试图启动一个tomcat v9 0服务器在本地主机上春天STS但它会弹出以下错误 本地主机启动tomcat v9 0服务器错误 本地主机起tomcat服务器V9 0遇到了问题 没有使用的端口8080 所以这不应该是8080端口没有任何进程
  • Mac 10.15下安装brew

    在Mac下初次使用brew命令会出现 bash brew command not found 随后找了各大博客 要在命令行输入如下命令 bin zsh c curl fsSL https gitee com cunkai HomebrewC
  • 历年研究生数学建模优秀论文汇总

    全国研究生数学建模竞赛 National Post Graduate Mathematical Contest in Modeling 是 全国研究生创新实践系列活动 的主题赛事之一 一般位于九月中旬 历时四天 竞赛题目一般来源于工程与管理
  • 卷积神经网络实现人脸表情识别

    文章目录 一 实现过程 二 运用训练的模型实现表情识别 一 实现过程 1 1 下载数据集 https github com truongnmt smile detection 1 2 根据猫狗数据集训练的方法来训练笑脸数据集 coding
  • HBase介绍(列存储)

    HBase介绍 列存储 2013 11 26 23 25 5871人阅读 评论 2 收藏 举报 分类 云存储 2 Hbase简介 started by chad walters and jim 2006 11 G release paper
  • FPGA-UART串口通信

    目录 前言 1 UART串口的介绍 2 实验开始前一些参数的计算 3 UART通信的时序 4 代码部分 1 接收部分代码 2 发送部分的代码 前言 本篇文章是为了记录自己FPGA的学习过程 不完全正确 仅供参考 1 UART串口的介绍 ua
  • 02守护进程学习之创建守护进程的七步骤及其分析

    02守护进程学习之创建守护进程的七步骤及其分析 与守护进程相关的文章 01守护进程学习之会话的概念和创建会话 包含Linux下相应id的总结一览 02守护进程学习之创建守护进程的七步骤及其分析 03守护进程学习之创建守护进程的代码例子 1
  • unicode,decode,encode在python的作用

    字符串在Python内部的表示是unicode编码 因此 在做编码转换时 通常需要以unicode作为中间编码 即先将其他编码的字符串解码 decode 成unicode 再从unicode编码 encode 成另一种编码 即 其他编码 g
  • 爬取起点网站图书信息(书名、作者、简介、图片url)

    爬取qidian网站图书信息 书名 作者 简介 图片url import requests from lxml import etree import json class BookSpider object def init self s
  • 华为s5720s-28p-si-ac加睿易路由器VLAN路由设置

    华为s5720s 28p si ac加睿易路由器VLAN路由设置 1 华为交换机初始化设置好WEB接口 2 自带易维WEB版划分VLAN 3 制作好VLAN网关 4 设置好相关接口 5 设置好DHCP以便分VLAN能自动获取到地址 6 进入
  • JS控制台报错Uncaught TypeError Cannot read properties of null (reading ‘appendChild‘);的解决方法

    当控制台出现Uncaught TypeError Cannot read properties of null reading appendChild 解决方法 将html文件中的script标签放在最下面或者中间
  • gethostbyname ()函数的使用

    gethostbyname 函数 根据主机名获取主机信息 用域名或者主机名获取地址 操作系统提供的库函数 函数原型 struct hostent gethostbyname const char hostname hostent结构体 st
  • Chrome下载文件错误:收到了来自服务器的重复标头

    在Chrome浏览器中下载文件时出现错误 收到了来自服务器的重复标头 然而在IE和Firefox浏览器中则不会出现此错误 经上网查看各位前辈的记录后 找到了自己项目的原因 response setHeader Content disposi
  • 使用Express快速搭建静态资源服务器

    有时候 客户端程序实现了某些功能需要与服务端联调 比如从服务器下载一些静态资源文件 XML JSON EXE HTML JS CSS等 像前文提到的场景 测试Electron程序的自动升级功能 我们介绍了如何使用Minio 不用写一行代码就
  • python-数据可视化-使用API

    使用Web应用程序编程接口 API 自动请求网站的特定信息而不是整个网页 再对这些信息进行可视化 一 使用Web API Web API是网站的一部分 用于与使用具体URL请求特定信息的程序交互 这种请求称为API调用 请求的数据将以易于处
  • 【知识分享】数据结构的应用——链表

    背景 对于数据结构 其实学过C语言的都不陌生 无外乎就队列 栈 二叉树等等这些 但其实对于初学者最困惑的不是数据结构是怎么样的 而是数据结构有什么用 我自己也是工作好几年后才体验到数据结构的快乐 所以本系列文章重点从应用场景切入 让大家理解