本篇讲解数据结构方面,我只是想粗略了解,本科没有学过,不感兴趣的读者请略过此篇
注:此篇部分内容引用自解学武的内容
此部分因为涉及到本科没学过的内容,需要算法的知识,所以只简单记下,待日后有编写到相关程序,再来补充。
文章是我前几天读了朱有鹏,张先凤老师的《嵌入式Linux与物联网软件开发:C语言内核深度解析》写的,拜读之后,虽没有醍醐灌顶,至少解开了我之前的一些疑惑。
以前学C语言,就是在IDE上编一下代码,编译器会有错误有警告提示,很少思考过变量、指针、结构体、函数之间,及所编代码和当前所运行系统的关系(系统内存有多大,运行速度怎样,怎样优化算法)。等我真正学了嵌入式,才开始思考上面的问题,才开始去了解软件与硬件的关系。
本文章是边复习边记笔记,跟书籍目录不一样,以后可能会补充及修改。
有纰漏请指出,转载请说明。
学习交流请发邮件 1280253714@qq.com
第九章 数据结构&链表&状态机&多线程
数据结构
数据结构是什么
数组是最简单的数据结构,链表复杂一些,二叉树、图则是更加复杂的数据结构。
数据结构由简单到复杂,它们所要解决的问题也由简单到复杂。要学习复杂的数据结构就要先学习简单的数据结构。
学好数据结构的难点在于:存储数据的同时还能将数据之间的关系也存储起来。
如:导航软件(比如高德、腾讯地图等),要想实现精确导航,软件必须存储大量的数据,包括各个省、市、区、县的道路、建筑物、红绿灯等位置信息,以及每个地区的天气信息、高速路况信息等等。
数据结构的存储结构
集中存储:方便后续查找数据
分散存储:方便后续增加或删除数据
数据结构的逻辑结构
无关系(查找表)
![](https://i-blog.csdnimg.cn/blog_migrate/cc90ca6604618ee558fb335d80a90d37.png)
一对一(线性存储结构)
![](https://i-blog.csdnimg.cn/blog_migrate/56ab7439cc92b197f0fc01c451d768ad.png)
一对多(树存储结构)
![](https://i-blog.csdnimg.cn/blog_migrate/21b95c115a8b542fea62cfcb0da5dc41.png)
多对多(图存储结构)
![](https://i-blog.csdnimg.cn/blog_migrate/395d67d0459aaf3a4d3e353c44e5ef9a.png)
数据结构知识图谱
![](https://i-blog.csdnimg.cn/blog_migrate/e0e9c0027b3ceb81625c5a540195335b.gif)
数据结构和算法的关系
对数据集{2,4,5,1,3}做升序排序,解决这个问题的算法就有很多种,比如冒泡排序算法、快速排序算法、归并排序算法、希尔排序算法等。借助任何一种算法,都可以得到{1,2,3,4,5}升序序列。同一个问题,使用不同的算法,虽然都可以解决问题,但有的算法执行效率高,有的算法执行效率低。
通常情况下,挑选算法主要考虑两个方面,分别是:
执行效率:根据算法编写出的程序,执行时间越短,效率就越高;
占用的内存空间:不同算法编写出的程序,执行时占用的内存空间也不相同。如果实际场景中仅能使用少量的内存空间,就要优先选择占用空间最少的算法。
算法只有两三个时,可以编写出每个算法,在计算机执行,计算各自执行时间和占用空间大小。实际开发中,往往采用 “预先估值”的方法挑选算法。具体来讲,就是分析各个算法的实现过程(步骤),估算出它们各自的运行时间和占用的内存大小,就可以挑选出“最好”的算法。
我们习惯用「时间复杂度」预估算法的执行时间,用「空间复杂度」预估算法占用的内存大小。
顺序表
线性表有顺序表(连续存储)和链表(分散存储)
#include <stdio.h>
#include <stdlib.h>
#define Size 5 //对Size进行宏定义,表示顺序表的最大容量
typedef struct{
int* head;
int length;
int size;
}Table;
void initTable(Table * t) {
//构造一个空的顺序表,动态申请存储空间
t->head = (int*)malloc(Size * sizeof(int));
//如果申请失败,作出提示并直接退出程序
if (!t->head)
{
printf("初始化失败");
exit(0);
}
//空表的长度初始化为0
t->length = 0;
//空表的初始存储空间为Size
t->size = Size;
}
//输出顺序表中元素的函数
void displayTable(Table t) {
int i;
for (i = 0; i < t.length; i++) {
printf("%d ", t.head[i]);
}
printf("\n");
}
int main() {
int i;
Table t = { NULL,0,0 };
initTable(&t);
//向顺序表中添加{1,2,3,4,5}
for (i = 1; i <= Size; i++) {
t.head[i - 1] = i;
t.length++;
}
printf("顺序表中存储的元素分别是:\n");
displayTable(t);
free(t.head);//释放申请的堆内存
return 0;
}
链表的引入
为什么需要链表
C++支持变长数组,就是在内存中申请一块更大的数组,将原来数组的内容复制到新申请数组的起始部分,删除原来的数组。顺序表同样在表中间增删时也要改动其后元素的位置。二者在进行元素的插入和删除时,操作效率低。
如果简单的数据结构可以解决问题,就没有必要使用复杂的数据结构。数组和顺序表天生的缺陷导致它解决不了某些问题,所以人们才发明了链表。
链表与数组
数组的优点是操作简单,易于理解,而且不需要开辟额外的空间,但是在数组中间进行插入和删除的操作,其效率比较低。
链表的优点是操作灵活,插入删除效率很高,但是缺点是需要额外分配存放节点地址的空间,而且操作有些繁琐。
单链表的实现
结点
![](https://i-blog.csdnimg.cn/blog_migrate/f04fb3727779c1384c31f5436492d240.png)
最简单的单链表
单链表的创建到使用的大致步骤如下。
创建空的单链表,比如可以定义一个 creat_node 函数来创建第一个节点
操作单链表(增添、删除、查找、更改、排序),比如插入操作,定义一个 insert_tail() 函数来向链表(或是节点的后面追加新节点。
销毁单链表,比如定义一个 destroy_list() 函数,用于销毁链表。
由上面的步骤可知,构建一个简单链表至少需要两个步骤:创建空链表,以及增添节点。
单链表上的每个元素都是结点,结点都由数据源和指针域构成
#include <stdio.h>
struct ListNode{
int val;
struct ListNode *next;
};
int main(){
struct ListNode a,b,c,d,e;
a.val=1;
b.val=2;
c.val=3;
d.val=4;
e.val=5;
a.next=&b;
b.next=&c;
c.next=&d;
d.next=&e;
e.next=NULL;
struct ListNode *head=&a;
while(head){
printf("val=%d\t addr=%p\t next=%p\n",head->val,head,head->next);
head=head->next;
}
return 0;
}
https://www.bilibili.com/video/BV1Gi4y1w7yG?t=205.6
val=1 add=20000408 next=20000400
val=2 add=20000400 next=200003f8
val=3 add=200003f8 next=200003f0
val=4 add=200003f0 next=200003e8
val=5 add=200003e8 next=00000000
![](https://i-blog.csdnimg.cn/blog_migrate/9d04d47f219bc6d00caf6f45854ff4a7.png)
双向链表
虽然单链表能 100% 存储逻辑关系为 "一对一" 的数据,但在解决某些实际问题时,单链表的执行效率并不高。例如,若实际问题中需要频繁地查找某个结点的前驱结点,使用单链表存储数据显然没有优势,因为单链表的强项是从前往后查找目标元素,不擅长从后往前查找元素。
解决此类问题,可以建立双向链表(简称双链表)。
状态机
考虑状态机的关键点:当前状态、外部输入、下一状态
水的三态
![](https://i-blog.csdnimg.cn/blog_migrate/a785f507a531a498629d5443043382fd.png)
鼠标点击图标的状态(可能是以下的情况,用到了状态机。或者是用了更为高级的程序,不知道,以后也许会知道)
![](https://i-blog.csdnimg.cn/blog_migrate/a77c6f0ae2ca454748c13a6a2943ab3e.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/1f7e9a2070a36ef73fd2885f4f8a9a45.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6bd112e57638f432ca3ff402259d29df.png)
多线程
OS下的并行执行
单核CPU:宏观并行,微观串行
多核CPU:微观并行,提高宏观上的并行度
进程和线程
https://blog.csdn.net/weixin_48271092/article/details/123649795
通俗理解:进程是打开的PPT,线程是PPT下的服务程序
![](https://i-blog.csdnimg.cn/blog_migrate/eab1ebc6af59aa41fc9dcdcb47e02cf3.png)