嵌入式第十八课!!数据结构篇入门及单向链表

在前几章对C语言的学习中,我们学到了:

  1. 基本的C语法和简单算法
  2. 面向过程的编程思想

而在数据结构这一篇章,我们将要学习:

  1. 常用的数据存储结构
  2. 算法
  3. 面向对象的编程思想

数据结构

在正式开始学习之前,我们先来了解一下什么是数据结构:

什么是数据结构?

所谓数据结构,就是用来组织和存储数据的,而存储 , 即存储变量、数组(在数据结构里称为顺序表)等;程序 = 数据结构 + 算法,了解数据的存放佐以数据的算法,就构成了程序。

数据与数据之间的关系

1)逻辑结构 :数据元素与元素之间的关系

  •  集合:元素与元素之间平等独立的集合关系
  • 线性结构:数据元素与元素之间存在一对一的关系(顺序表、链表、队列、栈)
  • 树形结构:数据元素与元素之间存在一对多的关系(二叉树)
  • 图形结构:数据元素与元素之间存在多对多的关系 (网状结构)

2)物理结构 :数据元素在计算机内存中的存储方式

顺序结构:

在内存中选用一段连续的内存空间(线性结构)进行存储

  1.  数据访问方便(O(1))
  2. 插入和删除数据时需要移动大量数据
  3. 需要预内存分配
  4. 可能造成大量的内存碎片  

内存碎片:

  • 内内存碎片:如结构体struct里空出的空间;
  • 外内存碎片:申请大连续空间(malloc)剩余的空间;

顺序结构的存储如图所示:

像数组一样,依次进入存储空间,如果要插入数据或删除数据都要把该数据前后的数据修改一下。

链式结构:

可以在内存中选用一段非连续的内存空间进行存储

  1. 数据访问时必须要从头遍历(O(n))
  2. 插入和删除元素方便
  3. 不需要预内存分配,是一种动态存储的方式
  4. 可以充分使用内存空间

链式结构的存储方式如图所示:

链式结构存储是通过元素后的指针指向下一元素进行链接的,当最后一个元素后的指针是一个空指针时就表示停止。

顺序结构和链式结构的时间复杂度如图所示:

索引结构:

将要存储的数据的关键字和存储位置之间构建一个索引表,也就是给下面的哈希函数建立一个表。

散列结构(哈希结构):

将数据的存储位置与数据元素之间的关键字建立起对应的关系(哈数函数),根据该关系进行数据存储和快速查找

哈希结构存储方式如下图:

中间关键字key 和addr的表就是索引结构

基本功能

数据结构的基本功能就是:

  1. 快捷使用指针
  2. 结构体(封装)
  3. 实现动态内存分配

数据结构的内容

数据结构包含一下内容(带*的为重点)

     1. 链式表
单向链表*
双向链表*
循环链表
内核链表
2. 栈*
3. 队列*
4. 二叉树
5. 哈希表
6.常用算法*

单向链表

单向链表的组成

链表:对象(存储数据的对象)、属性(变量)、行为(函数)

单向链表是由链表对象与各个结点组成的:

结点:

单向链表的结点是由上方的数据域data和下方的指针域pnext(指向下一个结点)组成的。

链表对象

链表对象是由phead(保存头结点地址,指向下一节点),clen(节点个数),以及其他属性组成的。

单向链表代码

1. 创建链表对象

首先我们要先在声明  link . h  里封装结构体:

#ifndef __LINK_H__
#define __LINK_H__/*链表存储的数据的数据类型*/
typedef int Data_type_t;/*链表的结点类型*/
typedef struct node
{Data_type_t data;struct node *pnext;
}Node_t;/*链表对象类型*/
typedef struct link
{Node_t *phead;int clen;
}Link_t;
#endif

然后我们在函数文件里创建函数:

#include "link.h"
#include <stdlib.h>
#include <stdio.h>/*创建单向链表*/
Link_t *create_link()
{Link_t *plink = malloc(sizeof(Link_t));if (NULL == plink){printf("malloc error!\n");return NULL;}plink->phead = NULL;plink->clen = 0;return plink;
}

在堆区开辟空间创建链表对象,如果没有申请到空间,即打印出错;如果申请成功,将链表对象指向结点的指针初始化为空指针,节点个数设置为0;

在主函数调用函数:

#include <stdio.h>
#include "link.h"int main(int argc, const char *argv[])
{Link_t *plink = create_link();if (NULL == plink){return -1;}return 0;
}

2. 插入数据(头插、尾插)

头插

头插即是从链表对象后开始插入数据:

/*向单向链表的头部插入数据*/
int insert_link_head(Link_t *plink, Data_type_t data)
{//申请结点Node_t *pnode = malloc(sizeof(Node_t));if (NULL == pnode){printf("malloc error!");return -1;}//初始化结点pnode->data = data;pnode->pnext = NULL;//头插2步插入结点pnode->pnext = plink->phead;plink->phead = pnode;plink->clen++;return 0;
}

首先,申请空间存放节点,后对结点进行初始化;我们要先让待插入的结点指向当前链表对象指向的结点;后再让链表对象指向当前待插入的结点。不然如果先让链表对象指向待插入的结点,后一位结点的位置将会丢失,不能再进行链接:

尾插

尾插即是从链表末尾开始插入数据:

int insert_link_back(Link_t *plink , Data_type_t data)
{Node_t *pnode = malloc(sizeof(Node_t));if(NULL == pnode){printf("malloc error!");return -1;}pnode -> data = data;pnode -> pnext =NULL;if(plink -> phead == NULL){plink -> phead = pnode;pnode -> pnext = NULL;plink -> clen++;return 0;}else{Node_t *ptmp = plink -> phead;while(ptmp -> pnext != NULL){ptmp =  ptmp-> pnext;}ptmp -> pnext = pnode;plink ->clen++;return 0;}

尾插的一些逻辑算法和头插一致,但不同的是,先判断是否是一个空链表:如果是一个空链表,那就直接改变链表对象的指向与结点的指向即可;如果不是一个空指针,要先遍历一遍结点,找到末尾的结点,修改它的指针域指向待插入结点。

3. 删除数据(头删、尾删)

头删

头删即是从链表对象指向的结点开始销毁空间:

int delete_link(Link_t *plink)
{Node_t*ptmp = plink -> phead;plink -> phead = ptmp -> pnext;if(plink -> phead == NULL){return 0;}else{free(ptmp);plink -> clen--;ptmp = NULL;return 1;}
}

先设定一个指针来指向第一个结点,此时设定链表对象指向下一个结点;如果此时是一个空链表,即不再运行、如果非空链表,就解放当前指针ptmp指向的第一个结点的空间,同时让节点个数-1,别忘了此时ptmp是野指针,将ptmp设为空指针。

尾删

尾删即是从链表的最后一个结点开始删除:

int delete_linkback(Link_t *plink)
{if (plink -> phead == NULL){free(plink);return -1;}Node_t*ptmp = plink -> phead;if (ptmp -> pnext == NULL){delete_link(plink);return 0;}else{while(ptmp -> pnext -> pnext != NULL){ptmp = ptmp -> pnext;}free(ptmp -> pnext);plink -> clen--;ptmp -> pnext = NULL;return 1;}
}

这种删除数据的方法要找到尾部结点的前一个结点,故使用ptmp寻找它指向结点的下一结点的指向是否指向空指针;如果不是,则继续遍历、如果是,则解放当前指针指向结点的下一结点;同时要记得判断空链表与链表只有一个节点的情况;

4. 查找数据

查找数据时要遍历所有结点的数据域,然后输出找到的地址:

Node_t *find_tdata(Link_t *plink , Data_type_t n)
{Node_t *ptmp = plink->phead;while(ptmp != NULL){if(n == ptmp -> data){return ptmp;}ptmp = ptmp -> pnext;}return ptmp;}

这个只需要先让指针指向第一个结点,后一一遍历比较即可。

5. 修改数据

修改数据可以直接调用查找数据的函数,然后直接改变其结点的数据域即可:

Node_t *find_tdata(Link_t *plink , Data_type_t n)
{Node_t *ptmp = plink->phead;while(ptmp != NULL){if(n == ptmp -> data){return ptmp;}ptmp = ptmp -> pnext;}return ptmp;}
int change_linkdata(Link_t *plink , Data_type_t oldata , Data_type_t newdata)
{Node_t*ptmp = find_tdata(plink , oldata);if(ptmp == NULL){return 0;}else{ptmp -> data = newdata;return 1;}
}

6. 销毁链表

销毁链表不能直接使用free函数调用指向链表的指针plink,这样会导致剩余的结点的位置全部丢失,导致剩下很多结点的存储空间未被销毁。所以要先使用删除数据,销毁所有结点空间,后再进行销毁链表对象空间:

Link_t *destroy_link(Link_t *plink)
{int n;if(plink -> phead == NULL){free(plink);plink = NULL;return plink;}else{while(plink -> phead != NULL){n = delete_link(plink);if(n == 0){plink = NULL;return plink;}}}
}

调用删除数据函数进行循环,直到链表变为空链表为止,后进行销毁;

以上就是和大家分享的内容!!!!单向链表的理解需要指针扎实的基础和结构体的应用,感谢你的阅读!!!!如有遗漏和错误,欢迎评论区进行批评和指正!!

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

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

相关文章

win10任务栏出问题了,原来是wincompressbar导致的

问题描述兄弟们客户说自己电脑现在有问题了&#xff0c;任务栏显示的都不对&#xff0c;和之前的都不一样&#xff0c;现在使用起来非常难受&#xff0c;我们来看一下&#xff0c;这到底是什么问题吧&#xff01;到客户现场&#xff0c;查看发现&#xff0c;客户桌面系统最底下…

FFmpegHandler 功能解析,C语言程序化设计与C++面向对象设计的核心差异

FFmpegHandler 功能解析 本文件记录了关于 FFmpegHandler 类中核心函数工作流程的详细解释。Q: FFmpeg逐帧解码&#xff0c;FFmpegHandler::openVideo 和 FFmpegHandler::readAVFrame 这两个函数都分别做了什么&#xff1f; A: 可以把整个过程想象成“准备播放一部电影”&#…

Codeforces Round 1039 (Div. 2) A-C

A. Recycling Center题目大意 给你n个垃圾袋&#xff0c;每个垃圾袋有一个重量 在每秒钟&#xff0c;你可以选择一个垃圾袋&#xff0c;如果他的重量小于等于c&#xff0c;那么你可以不花费硬币丢掉它 当你丢掉一个垃圾袋后&#xff0c;其他垃圾袋在这一秒重量会翻倍 问最少花费…

【设计模式】 原则

单一职责原则 对于一个类而言&#xff0c;有且仅有一个引起他变化的原因或者说&#xff0c;一个类只负责一个职责 如果一个类承担的职责过多&#xff0c;那么这些职责放在一起耦合度太高了&#xff0c;一个职责的变化可能会影响这个类其他职责的能力。 所以我们在做软件设计的时…

windows11右键菜单新增项增加drawio文件,使用draw.io

目录1.新建空白模板2.建立注册表文件1.新建空白模板 这里我们的模板文件路径为 D:\Software\drawio\template.drawio 2.建立注册表文件 首先新建一个.txt文件&#xff0c;我这里取名为menulize.txt&#xff0c;然后将下面的内容复制到.txt文件中 Windows Registry Editor Ver…

解锁网页魔法:零基础HTML通关秘籍

文章目录**解锁网页魔法&#xff1a;零基础HTML通关秘籍**HTML 基础目标HTML 结构认识 HTML 标签HTML 文件基本结构标签层次结构快速生成代码框架HTML 常见标签注释标签注释的原则标题标签: h1-h6段落标签: p换行标签&#xff1a;br综合案例: 展示博客超链接标签: a表格标签**基…

类似 Pixso 但更侧重「网页 / 软件界面设计」「前后端可视化开发」的工具

从 GoView 的 Demo 功能来看&#xff0c;它主要聚焦于数据可视化大屏的低代码搭建&#xff0c;更侧重数据图表配置和页面布局&#xff0c;没有类似 Pixso 的在线 UI 设计&#xff08;如矢量绘图、组件样式精细化设计&#xff09;功能&#xff0c;其核心是通过预设组件快速构建数…

MySQL--组从复制的详解及功能演练

2.MySQL的组从复制 2.1 配置mastesr [rootmysqlaa ~]# vim /etc/my.cnf [mysqld] server-id10 datadir/data/mysql socket/data/mysql/mysql.sock default_authentication_pluginmysql_native_password log-binmysql-bin[rootmysqlaa ~]# /etc/init.d/mysqld restart# 进入数据…

JavaScript将String转为base64 笔记250802

JavaScript将String转为base64 笔记250802 在 JavaScript 中将字符串转换为 Base64 编码有多种方法&#xff0c;每种方法都有其适用场景。下面我将全面介绍这些方法&#xff0c;包括处理 ASCII 字符、Unicode 字符以及性能优化方案。 基础方法&#xff1a;btoa() 基本用法&a…

Unity3D数学第四篇:射线与碰撞检测(交互基础篇)

Unity3D数学第一篇&#xff1a;向量与点、线、面&#xff08;基础篇&#xff09; Unity3D数学第二篇&#xff1a;旋转与欧拉角、四元数&#xff08;核心变换篇&#xff09; Unity3D数学第三篇&#xff1a;坐标系与变换矩阵&#xff08;空间转换篇&#xff09; Unity3D数学第…

数据处理和统计分析——09 数据分组

1 聚合 1.1 简介 在SQL中我们经常使用GROUP BY将某个字段&#xff0c;按不同的取值进行分组&#xff0c;在Pandas中也有groupby()函数&#xff1b;分组之后&#xff0c;每组都会有至少1条数据&#xff0c;将这些数据进一步处理返回单个值的过程就是聚合&#xff0c;比如分组之后…

【数据结构与算法】数据结构初阶:排序内容加餐(一)——快速排序:三路划分、自省排序

&#x1f525;个人主页&#xff1a;艾莉丝努力练剑 ❄专栏传送门&#xff1a;《C语言》、《数据结构与算法》、C语言刷题12天IO强训、LeetCode代码强化刷题 &#x1f349;学习方向&#xff1a;C/C方向 ⭐️人生格言&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为…

MySqL(加餐)

范式第一范式数据库表的每一列都是不可分割的原子数据项&#xff0c;而不能是集合&#xff0c;数组&#xff0c;对象等非原子数据。在关系型数据库的设计中&#xff0c;满足第一范式是对关系模式的基本要求。不满足第一范式的数据库就不能被称为关系数据库。第一范式实际上只要…

【redis】基于工业界技术分享的内容总结

Redis 实践指南与核心概念 一、Java 中常用的 Redis 使用场景与实践 缓存&#xff08;Caching&#xff09; 场景&#xff1a;热点数据、频繁访问的数据&#xff0c;如商品详情、用户信息。通过缓存减少数据库压力&#xff0c;提高系统响应速度。 工业界实践&#xff1a; 淘宝…

服务端之nestJS常用异常类及封装自定义响应模块

MENU前言常用异常类&#xff08;由nestjs/common提供&#xff09;示例自定义异常&#xff08;可选&#xff09;自定义响应模块前言 在NestJS中&#xff0c;nestjs/common提供了大量的内置异常类&#xff0c;主要用于在控制器、服务等层抛出特定的HTTP错误响应。 常用异常类&…

数据链路层、NAT、代理服务、内网穿透

目录 一. 以太网 以太网帧格式 二. MAC地址 三. MTU 四. ARP协议 五. NAT NAPT 六. 代理服务器 正向代理 反向代理 七. 内网穿透 八. 内网打洞 一. 以太网 • "以太网" 不是一种具体的网络, 而是一种技术标准; 既包含了数据链路层的内 容, 也包含了一些物理层…

Rust在CentOS 6上的移植

Rust已不支持Cent OS 6 rhel是Redhat 发布的Red Hat Enterprise Linux的简称&#xff0c;使用rhel源代码编译的CentOS&#xff0c;最新的版本是CentOS 7&#xff0c;于2024年停止支持。而更古老的CentOS 6&#xff0c;则在2020年就已经结束了。 而面对如此老旧的系统&#xf…

C++音视频开发:基础面试题

音视频领域技术门槛高&#xff0c;学习资料稀缺&#xff0c;体系化书籍和开发工具有限&#xff0c;新手入门困难。音视频开发涉及众多任务&#xff1a;音频&#xff08;采集、编解码、降噪等&#xff09;、视频&#xff08;采集、编解码、图像处理&#xff09;、实时传输&#…

C++刷题 - 7.27

贪心算法的详细逻辑这个问题的最优解可以用 贪心算法 在 O(N) 时间 内解决。它的核心思想是&#xff1a;每次操作尽可能覆盖最长的连续非零区间&#xff0c;并通过数学分析发现&#xff1a;最小操作次数等于所有“上升台阶”的高度差之和。1. 直观理解假设 steps [1, 2, 3, 2,…

音频3A处理简介之AGC(自动增益控制)

在音频通话和视频会议中&#xff0c;音频自动增益控制AGC模块的主要作用&#xff1a;• 稳定音频信号的输出电平。无论麦克风采集信号的强弱&#xff08;如用户离麦克风远近程度不同&#xff09;&#xff0c;尽可能保证音频采集模块的输出音量保持相对一致&#xff0c;不会偏大…