数据结构——二叉树(堆)

大家好我是小峰,今天我们开始学习二叉树。

首先我们来学习什么是树?

树概念及结构

树是一种 非线性 的数据结构,它是由 n n>=0 )个有限结点组成一个具有层次关系的集合。 把它叫做树是因 为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的
有一个 特殊的结点,称为根结点 ,根节点没有前驱结点
除根节点外, 其余结点被分成 M(M>0) 个互不相交的集合 T1 T2 …… Tm ,其中每一个集合 Ti(1<= i <= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有 0 个或多个后继
因此, 树是递归定义 的。
树类似于下面的结构
注意:树形结构中,子树之间不能有交集,否则就不是树形结构(这种结构叫做图)

树的相关概念

我们下面来看看树的结构于概念

节点的度 :一个节点含有的子树的个数称为该节点的度; 如上图: A 的为 6
叶节点或终端节点 :度为 0 的节点称为叶节点; 如上图: B C H I... 等节点为叶节点
非终端节点或分支节点 :度不为 0 的节点; 如上图: D E F G... 等节点为分支节点
双亲节点或父节点 :若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图: A B 的父节点
孩子节点或子节点 :一个节点含有的子树的根节点称为该节点的子节点; 如上图: B A 的孩子节点
兄弟节点 :具有相同父节点的节点互称为兄弟节点; 如上图: B C 是兄弟节点
树的度 :一棵树中,最大的节点的度称为树的度; 如上图:树的度为 6
节点的层次 :从根开始定义起,根为第 1 层,根的子节点为第 2 层,以此类推;
树的高度或深度 :树中节点的最大层次; 如上图:树的高度为 4
堂兄弟节点 :双亲在同一层的节点互为堂兄弟;如上图: H I 互为兄弟节点
节点的祖先 :从根到该节点所经分支上的所有节点;如上图: A 是所有节点的祖先
子孙 :以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是 A 的子孙
森林 :由 m m>0 )棵互不相交的树的集合称为森林;
所以我们可得出结论:树的概念是基于人类亲缘关系的出来的,我们可以根据这一点来理解记忆

树的表示

树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了, 既然保存值域,也要保存结点和结点之间 的关系 ,实际中树有很多种表示方式如:双亲表示法,孩子表示法、孩子双亲表示法以及孩子兄弟表示法等。我们这里就简单的了解其中最常用的孩子兄弟表示法
这种方式的图解如下

思路是创建一个链表结构,节点中存储第一个孩子的节点,和下一个兄弟节点,这样不论有多少分支我们都可以表示出来。

接下来我们来学习一个特殊的树。

二叉树概念及结构

一棵二叉树是结点的一个有限集合,该集合 :
1. 或者为空
2. 由一个根节点加上两棵别称为左子树和右子树的二叉树组成
1. 二叉树不存在度大于 2 的结点
2. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树
从上图我们可以看出二叉树都是由以下这几种情况合并而成

特殊的二叉树:

1. 满二叉树 :一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K ,且结点总数是2^k-1 ,则它就是满二叉树。
2. 完全二叉树 :完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为 K的,有n 个结点的二叉树,当且仅当其每一个结点都与深度为 K 的满二叉树中编号从 1 n 的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。
下面我们来看看二叉树的一些性质
对于具有 n 个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从 0 开始编号,则对
于序号为 i 的结点有: 1. i>0 i 位置节点的双亲序号: (i-1)/2 i=0 i 为根节点编号,无双亲节点
2. 2i+1<n ,左孩子序号: 2i+1 2i+1>=n 否则无左孩子
3. 2i+2<n ,右孩子序号: 2i+2 2i+2>=n 否则无右孩子

二叉树的存储结构

1. 顺序存储
顺序结构存储就是使用 数组来存储 ,一般使用数组 只适合表示完全二叉树 ,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储,关于堆我们后面的章节会专门讲解。二叉树顺 序存储在物理上是一个数组,在逻辑上是一颗二叉树。
如下图
链式存储
二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。链式结构又分为二叉链和三叉链,当前我们学习中一般都是二叉链,后面学到高阶数据结构如红黑树等会用到三叉链。

二叉树的顺序结构及实现

我们通过上面的学习是不是对二叉树有了一定的理解现在我们用顺序结构来实现一个二叉树

我们来看看二叉树顺序结构的性质

在此之前我们先来看看堆的概念和结构

堆的概念及结构
如果有一个关键码的集合 K = { , , , ,},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足: <= 且 <= ( >= 且 >= ) i = 0, 1 ,2…,则称为小堆 ( 或大堆 ) 。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
堆的性质:
堆中某个节点的值总是不大于或不小于其父节点的值;
堆总是一棵完全二叉树。

大堆的父亲总是不大于孩子,小堆的父亲总是不小于孩子

现在我们来实现一个堆(这里我们举例实现大堆

堆的实现

因为每一个堆都是完全二叉树,所以我们用数组来实现一个堆,

这里我们要明白堆的物理结构是数组而堆的逻辑结构是二叉树,要时刻记住。

首先我们还是分装三个文件

堆的实现思路主要是数组,所以我们用顺序表来实现,顺序表的玩法我们都已经很了解了,所以不用啰嗦了我们直接上代码

初始化和销毁

//初始化
void Heapinit(Heap* hp) {assert(hp);hp->capacity = 0;hp->ps = NULL;hp->sz = 0;
}//销毁
void HeapDestory(Heap* hp) {assert(hp);free(hp->ps);hp->ps = NULL;hp->capacity = 0;hp->sz = 0;
}

堆的插入

对于堆的插入我们知道顺序表插入的思路是直接插入,但是我们这里是堆插入我们要满足堆的概念(父亲总是不大于或不小于孩子),所以我们插入后要进行排序,

这里我们来看看逻辑图

我们来看看代码

//堆插入
void Heapush(Heap* hp, CMMlet n) {assert(hp);if (hp->sz == hp->capacity) {int net = hp->ps == NULL ? 4 : hp->capacity * 2;CMMlet* cur = (CMMlet*)realloc(hp->ps, sizeof(CMMlet) * net);if (cur == NULL) {printf("%s", strerror(errno));return;}hp->ps = cur;hp->capacity = net;}hp->ps[hp->sz] = n;hp->sz++;//向上堆排序Adjustup(hp->ps, hp->sz - 1);
}

下面是向上堆排序的代码

//替换函数
tety(CMMlet* p1, CMMlet* p2) {CMMlet n = *p1;*p1 = *p2;*p2 = n;
}
//向上堆排序
//ps待排数据地址,child待排数据下标
void Adjustup(CMMlet* ps, int child) {assert(ps);int n = (child - 1) / 2;while (child > 0) {if (ps[n] < ps[child]) {//替换函数tety(&ps[n], &ps[child]);}else {break;}child = n;n = (child - 1) / 2;}
}

判断堆为空(为空返回true)

这个已经是老玩法了我们直接上代码

//堆的判空
bool HeapEmpty(Heap* hp) {assert(hp);return hp->sz == 0;
}

堆的删除

删除堆是删除堆顶的数据,我们想想顺序表删除表头数据采用的是覆盖删除,如果我们这里也采用覆盖删除,覆盖删除后还要对所有元素重新排序,这种方法可行但不建议,因为所需的时间复杂度太高了,

所以我们这里采用的是先将堆顶元素与最后一个元素调换,删除了最后的元素后再进行向下排序是不是要简单得多啊?这里我们只需要对一个元素排序。

排序的逻辑如下

我们来看代码

//堆的删除
void Heappop(Heap* hp) {assert(hp);assert(!HeapEmpty(hp));//替换tety(&hp->ps[0], &hp->ps[hp->sz - 1]);//删除hp->sz--;//向下堆排序Ajustdown(hp->ps, hp->sz, 0);
}

下面是向下堆排序代码

//向下堆排序
//ps排序数据地址,size排序有效数据个数,mon待排数据下标
void Ajustdown(CMMlet* ps, int size, int mon) {int child = mon * 2 + 1;while (child<size) {//找出最大的孩子if (child+1<size && ps[child] < ps[child + 1]) {++child;}//判断是否符合堆的结构if (ps[mon] < ps[child]) {tety(ps[mon], ps[child]);mon = child;child = mon * 2 + 1;}else {break;}}}

获取堆顶数据

老玩法了我们直接上代码

//获取堆顶数据
CMMlet Heaptop(Heap* hp) {assert(hp);assert(!HeapEmpty(hp));return hp->ps[0];
}

获取堆的有效数据个数

关门,我们直接上代码

//获取堆有效数据个数
int Heapsize(Heap* hp) {assert(hp);return hp->sz;
}

我们来测试一下我们的堆

void csheap() {Heap hp;int add[] = { 12,34,56,78,23,31,54,76,90,1,2, };int a=sizeof(add) / sizeof(add[0]);//初始化Heapinit(&hp);//插入for (int i = 0; i < a; i++) {Heapush(&hp, add[i]);}Heappop(&hp);Heappop(&hp);Heappop(&hp);Heappop(&hp);HeapDestory(&hp);
}int main() {csheap();return 0;
}

执行插入

执行删除

我们调试看现象我们的堆是不是建成了。

堆的应用

堆排序

从我们实现堆的思路中我们可以看出堆的主要应用是排序

总结:升序:建大堆 降序:建小堆

对于排序的思路我们主要用利用堆删除思想来进行排序,我们主要使用向下堆排序法来排序,(向上堆排序法的复杂度太高了)

我们可以用代码实现一下

//排序数据
void addpx(int* add, int mon) {//用向下堆排序建堆for (int i = (mon-1-1)/2; i >=0; --i) {Ajustdown(add, mon, i);}//用堆的删除思路来排序int n = mon;while (n > 0) {int a = add[0];add[0] = add[n-1];add[n-1] = a;//用向下堆排序建堆再次排序n--;Ajustdown(add, n,0);}
}

TOP-K问题

TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。

比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。

对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都不能一下子全部加载到内存中)。

最佳的方式就是用堆来解决,基本思路如下:

1. 用数据集合中前K个元素来建堆 前k个最大的元素,则建小堆 前k个最小的元素,则建大堆

2. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素,将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素。

我们可以写一个函数来实现

下面函数是在一万个数据中找到五个最大的。

void CreateNDate()
{// 造数据int n = 10000;srand(time(0));const char* file = "data.txt";FILE* fin = fopen(file, "w");if (fin == NULL){perror("fopen error");return;}for (size_t i = 0; i < n; ++i){int x = rand() % 1000000;fprintf(fin, "%d\n", x);}fclose(fin);
}void PrintTopK(int k)
{const char* file = "data.txt";FILE* fout = fopen(file, "r");if (fout == NULL){perror("fopen error");return;}int* kminheap = (int*)malloc(sizeof(int) * k);if (kminheap == NULL){perror("malloc error");return;}for (int i = 0; i < k; i++){fscanf(fout, "%d", &kminheap[i]);}// 建小堆for (int i = (k - 1 - 1) / 2; i >= 0; i--){Ajustdown(kminheap, k, i);}int val = 0;while (!feof(fout)){fscanf(fout, "%d", &val);if (val > kminheap[0]){kminheap[0] = val;Ajustdown(kminheap, k, 0);}}for (int i = 0; i < k; i++){printf("%d ", kminheap[i]);}printf("\n");
}int main()
{CreateNDate();PrintTopK(5);return 0;
}

大家注意我们这建的是小堆所以我们的向下堆排序函数是建大堆的所以判断条件要改一下大于小于号。

这是一个测试函数我们写了一万个数据在文件中

我们看是不是找出来了,但由于数据太多了我们也不知道找出来的数据是不是最大的五个,

我们可以验证一下,

我直接更改文件中的数据,既然不知道找出的是不是最大的那我们就手动改五个最大的数不就行了吗

我们再运行试试

是不是找到了?

不知不觉本期内容已经将近尾声了,最后大家看看标准的二叉树大家参拜参拜,愿我们的数据结构都学得炉火纯青

下面是本期全部代码大家可以参考尝试一下

Heap.h

# define _CRT_SECURE_NO_WARNINGS 1
#pragma once# include<stdio.h>
# include<assert.h>
# include<string.h>
# include<stdlib.h>
# include<errno.h>
# include<stdbool.h>
# include<time.h>typedef struct heap Heap;
typedef int CMMlet;struct heap {CMMlet* ps;int sz;//顺序表的元素个数int capacity;//顺序表容量
};//初始化
void Heapinit(Heap* hp);
//销毁
void HeapDestory(Heap* hp);
//堆插入
void Heapush(Heap* hp,CMMlet n);
//向上堆排序
void Adjustup(CMMlet* ps, int child);
//ps待排数据地址,child待排数据下标//堆的判空
bool HeapEmpty(Heap* hp);//堆的删除
void Heappop(Heap* hp);//向下堆排序
void Ajustdown(CMMlet* ps, int size, int mon);
//ps排序数据地址,size排序有效数据个数,mon待排数据下标//获取堆顶数据
CMMlet Heaptop(Heap* hp);//获取堆有效数据个数
int Heapsize(Heap* hp);

Heap.c

# include "heap.h"//初始化
void Heapinit(Heap* hp) {assert(hp);hp->capacity = 0;hp->ps = NULL;hp->sz = 0;
}//销毁
void HeapDestory(Heap* hp) {assert(hp);free(hp->ps);hp->ps = NULL;hp->capacity = 0;hp->sz = 0;
}//替换函数
tety(CMMlet* p1, CMMlet* p2) {CMMlet n = *p1;*p1 = *p2;*p2 = n;
}
//向上堆排序
//ps待排数据地址,child待排数据下标
void Adjustup(CMMlet* ps, int child) {assert(ps);int n = (child - 1) / 2;while (child > 0) {if (ps[n] < ps[child]) {//替换函数tety(&ps[n], &ps[child]);}else {break;}child = n;n = (child - 1) / 2;}
}
//堆插入
void Heapush(Heap* hp, CMMlet n) {assert(hp);if (hp->sz == hp->capacity) {int net = hp->ps == NULL ? 4 : hp->capacity * 2;CMMlet* cur = (CMMlet*)realloc(hp->ps, sizeof(CMMlet) * net);if (cur == NULL) {printf("%s", strerror(errno));return;}hp->ps = cur;hp->capacity = net;}hp->ps[hp->sz] = n;hp->sz++;//向上堆排序Adjustup(hp->ps, hp->sz - 1);
}//堆的判空
bool HeapEmpty(Heap* hp) {assert(hp);return hp->sz == 0;
}//向下堆排序
//ps排序数据地址,size排序有效数据个数,mon待排数据下标
void Ajustdown(CMMlet* ps, int size, int mon) {int child = mon * 2 + 1;while (child<size) {//找出大的孩子if (child+1<size && ps[child] > ps[child + 1]) {++child;}//判断是否符合堆的结构if (ps[mon] > ps[child]) {tety(&ps[mon], &ps[child]);mon = child;child = mon * 2 + 1;}else {break;}}}
//堆的删除
void Heappop(Heap* hp) {assert(hp);assert(!HeapEmpty(hp));//替换tety(&hp->ps[0], &hp->ps[hp->sz - 1]);//删除hp->sz--;//向下堆排序Ajustdown(hp->ps, hp->sz, 0);
}//获取堆顶数据
CMMlet Heaptop(Heap* hp) {assert(hp);assert(!HeapEmpty(hp));return hp->ps[0];
}//获取堆有效数据个数
int Heapsize(Heap* hp) {assert(hp);return hp->sz;
}

test.c

# include"heap.h"排序数据
//void addpx(int* add, int mon) {
//	//用向下堆排序建堆
//	for (int i = (mon-1-1)/2; i >=0; --i) {
//		Ajustdown(add, mon, i);
//	}
//	//用堆的删除思路来排序
//	int n = mon;
//	while (n > 0) {
//		int a = add[0];
//		add[0] = add[n-1];
//		add[n-1] = a;
//		//用向下堆排序建堆再次排序
//		n--;
//		Ajustdown(add, n,0);
//	}
//}
//void csheap() {
//	Heap hp;
//	int add[] = { 12,34,56,78,23,31,54,76,90,1,2, };
//	int a=sizeof(add) / sizeof(add[0]);
//	addpx(add, a);
//}
//
//int main() {
//	csheap();
//
//
//
//	return 0;
//}void CreateNDate()
{// 造数据int n = 10000;srand(time(0));const char* file = "data.txt";FILE* fin = fopen(file, "w");if (fin == NULL){perror("fopen error");return;}for (size_t i = 0; i < n; ++i){int x = rand() % 1000000;fprintf(fin, "%d\n", x);}fclose(fin);
}void PrintTopK(int k)
{const char* file = "data.txt";FILE* fout = fopen(file, "r");if (fout == NULL){perror("fopen error");return;}int* kminheap = (int*)malloc(sizeof(int) * k);if (kminheap == NULL){perror("malloc error");return;}for (int i = 0; i < k; i++){fscanf(fout, "%d", &kminheap[i]);}// 建小堆for (int i = (k - 1 - 1) / 2; i >= 0; i--){Ajustdown(kminheap, k, i);}int val = 0;while (!feof(fout)){fscanf(fout, "%d", &val);if (val > kminheap[0]){kminheap[0] = val;Ajustdown(kminheap, k, 0);}}for (int i = 0; i < k; i++){printf("%d ", kminheap[i]);}printf("\n");
}int main()
{//CreateNDate();PrintTopK(5);return 0;
}

  以上就是全部内容了,如果有错误或者不足的地方欢迎大家给予建议。 

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

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

相关文章

【2024红明谷】三道Web题目的记录

红明谷 文章目录 红明谷Web1 | SOLVED LaterWeb2 | UNSOLVEDWeb3 | SOLVED 容器已经关咯&#xff0c;所以有些场景只能靠回忆描述啦&#xff0c;学习为主&#xff0c;题目只是一个载体~ 本次比赛学习为主&#xff0c;确实再一次感受到久违的web题目的魅力了&#xff0c;可能也是…

数据转换 | Matlab基于GASF格拉姆角和场一维数据转二维图像方法

目录 效果分析基本介绍程序设计参考资料获取方式 效果分析 基本介绍 基于GASF&#xff08;Gramian Angular Summation Field&#xff09;的方法&#xff0c;将一维数据转换为二维图像的步骤描述 标准化数据&#xff1a; 首先&#xff0c;对一维时序数据进行标准化处理&#xf…

MyBatis的基本应用

源码地址 01.MyBatis环境搭建 添加MyBatis的坐标 <!--mybatis坐标--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.9</version></dependency><!--mysql驱动坐…

『51单片机』蜂鸣器

&#x1f6a9; WRITE IN FRONT &#x1f6a9; &#x1f50e; 介绍&#xff1a;"謓泽"正在路上朝着"攻城狮"方向"前进四" &#x1f50e;&#x1f3c5; 荣誉&#xff1a;2021|2022年度博客之星物联网与嵌入式开发TOP5|TOP4、2021|2222年获评…

OpenHarmony实战:轻量级系统之配置其他子系统

除上述子系统之外&#xff0c;还有一些必要但是无需进行移植的子系统。如&#xff1a;分布式任务调度子系统、DFX子系统。 这些子系统添加方式比较简单&#xff0c;在“vendor/MyVendorCompany/MyProduct/config.json”文件中进行如下配置即可&#xff1a; {"subsystem&…

专有钉钉微应用埋点以及本地调试埋点总结

最近在对接浙政钉&#xff0c;稳定性监控、通用采集 SDK、基础埋点、基础埋点&#xff0c;每次发布上去&#xff0c;工作人员那边反馈抓取不到信息 稳定性监控代码、通用采集 SDK index.html <!-- 流量稳定监控 S 关于埋点上线打开--><script src"https://wpk-…

IDEA 中能提高开发效率的插件

目录 前言 插件 Rainbow Brackets AceJump POJO to JSON Json Helper MybatisX Maven Helper PlantUML Integration TONYYI Lingma 前言 IDEA 里又很多好用的插件可以帮助我们提升开发效率&#xff0c;这里罗列下自己开发过程中常用的插件&#xff0c;善于利用插件&…

【第十一届大唐杯全国大学生新一代信息通信技术大赛】赛题分析

赛道一 一等奖 7% 二等奖 15% 三等奖 25% 赛道二 参考文档&#xff1a; 《第十一届大唐杯全国大学生新一代信息通信技术大赛&#xff08;产教融合5G创新应用设计&#xff09;专项赛说明.pdf》 一等奖&#xff1a;7% 二等奖&#xff1a;10% 三等奖&#xff1a;20% 赛项一&am…

unity工程输出的log在哪里?

在编辑器里进行活动输出的log位置&#xff1a; C:\Users\username\AppData\Local\Unity\Editor\Editor.log ------------------------------------ 已经打包完成&#xff0c;形成的exe运行后的log位置&#xff1a; C:\Users\xxx用户\AppData\LocalLow\xx公司\xx项目

销售与营销的区别:从手中到心中

一、引言 在商界&#xff0c;销售和营销常常被视为同义词&#xff0c;但实际上它们各自扮演着不同的角色。简而言之&#xff0c;销售是将产品送到客户手里&#xff0c;而营销则是将产品送到客户心里。这种微妙的差异对于企业的成功至关重要。正如彼得德鲁克所说&#xff1a;“…

Redis安装-Docker

安装redis的docker容器 1、创建redis挂载目录 mkdir -p /liuchaoxu/redis/{data,conf}2、复制配置文件 在 /liuchaoxu/redis/conf 目录中创建文件 redis.conf&#xff0c;文件从 redis-6.2.7.tar.gz 中解压获取 修改默认配置(从上至下依次)&#xff1a; #bind 127.0.0.1 …

游戏引擎中的物理应用

一、 角色控制器 Character Controller和普通的动态对象&#xff08;Dynamic Actor &#xff09;是不同的&#xff0c;主要的三个特点是: 它拥有可控制的刚体间的交互假设它是有无穷的摩擦力&#xff08;可以站停在位置上&#xff09;&#xff0c;没有弹性加速和刹车几乎立即…

《QT实用小工具·十》本地存储空间大小控件

1、概述 源码放在文章末尾 本地存储空间大小控件&#xff0c;反应电脑存储情况&#xff1a; 可自动加载本地存储设备的总容量/已用容量。进度条显示已用容量。支持所有操作系统。增加U盘或者SD卡到达信号。 下面是demo演示&#xff1a; 项目部分代码如下&#xff1a; #if…

vue项目引入微信sdk: npm install weixin-js-sdk --save报错

网上查到要用淘宝的镜像 同事告知旧 域名&#xff1a;https://registry.npm.taobao.org/已经不能再使用 使用 npm config set registry http://registry.npmmirror.com

css心跳动画

图标引入 <img class"icon" src"heart.svg" alt"" srcset""> CSS代码 <style>.icon {animation:bpm 1s linear,pulse 0.75s 1s linear infinite;}keyframes pulse {from,75%,to {transform: scale(1);}25% {transform:…

极简云验证 download.php 文件读取漏洞复现

0x01 产品简介 极简云验证是一款开源的网络验证系统&#xff0c;支持多应用卡密生成&#xff1a;卡密生成 单码卡密 次数卡密 会员卡密 积分卡密、卡密管理 卡密长度 卡密封禁 批量生成 批量导出 自定义卡密前缀等&#xff1b;支持多应用多用户管理&#xff1a;应用备注 应用版…

智能仪器驱动企业数字化转型 迈向智慧未来!

在当今数字化时代&#xff0c;企业正面临着前所未有的挑战和机遇。为了在竞争激烈的市场中立足并实现可持续发展&#xff0c;数字化转型已成为企业的当务之急。智能仪器作为数字化转型的核心驱动力&#xff0c;以其卓越的性能和创新的技术&#xff0c;为企业开启了通向智慧未来…

C_C++数据的在内存中的分布

C/C内存分布 在编程世界中&#xff0c;C和C语言一直以其强大的性能和灵活性著称。然而&#xff0c;这种强大和灵活的背后&#xff0c;离不开对内存分布的深入理解和熟练掌握。本文将详细介绍C/C程序中的内存分布&#xff0c;包括栈、堆和全局变量的存储区域。下面是c/c中&…

基于OrangePi Zero2的智能家居项目(开发阶段)

智能家居项目的软件实现 紧接上文 基于OrangePi Zero2的智能家居项目&#xff08;准备阶段&#xff09;-CSDN博客 目录 一、项目整体设计 1.1项目整体设计 1.2具体划分 二、开发工作的前期准备 1、进行分类&#xff0c;并用Makefile文件进行管理 参考&#xff1a;自己创…

基于单片机的智能报站系统仿真设计

**单片机设计介绍&#xff0c;基于单片机的智能报站系统仿真设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的智能报站系统仿真设计概要是关于采用单片机技术实现公交车报站功能的系统设计概述。以下是对该设计的…