当前位置: 首页 > news >正文

【数据结构】树——链式存储二叉树的基础

写在前面

书接上文:【数据结构】树——顺序存储二叉树

本篇笔记主要讲解链式存储二叉树的主要思想、如何访问每个结点、结点之间的关联、如何递归查找每个结点,为后续更高级的树形结构打下基础。不了解树的小伙伴可以查看上文


文章目录

  • 写在前面
  • 一、链式二叉树的定义
  • 二、链式二叉树的遍历
    • 2.1、前序、中序以及后序遍历
        • 前序遍历的代码实现
        • 中序遍历的代码实现
        • 后序遍历的代码实现
  • 三、链式二叉树的元素个数
        • 代码实现
  • 四、链式二叉树的高度
        • 代码实现
  • 五、二叉树第k层节点个数
        • 代码实现
  • 六、二叉树查找值为x的节点
        • 代码实现
  • 七、链式存储二叉树的OJ
    • 7.1、单值二叉树
        • 代码实现
  • 八、二叉树的创建和销毁
        • 代码实现


一、链式二叉树的定义

二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成数据域和左右指针域左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址。(二叉树有二叉链和三叉链两种表示方式,本文主要讲解二叉链二叉链一般指孩子表示法,三叉连指孩子双亲表示法,这两种方式是二叉树最常见的表示方式,虽然还有孩子兄弟表示法,该中表示方式本质也是二叉链)

在这里插入图片描述在这里插入图片描述
链式存储的结构体:

// 二叉链
struct BinaryTreeNode
{struct BinTreeNode* _pLeft;// 指向当前节点左孩子struct BinTreeNode* _pRight; // 指向当前节点右孩子BTDataType _data; // 当前节点值域
};
// 三叉链
struct BinaryTreeNode
{struct BinTreeNode* _pParent; // 指向当前节点的双亲struct BinTreeNode* _pLeft;// 指向当前节点左孩子struct BinTreeNode* _pRight; // 指向当前节点右孩子BTDataType _data; // 当前节点值域
};
  • 上面的结构体就成功创建出链式存储的二叉树

二、链式二叉树的遍历

学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。( 不才默认左子树先运行后再运行右子树,再每次遍历先都需要规定一下左右子树的先后顺序。)
在这里插入图片描述
按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历:

2.1、前序、中序以及后序遍历

前序、中序以及后序遍历的思想都是相同的,只是根节点访问的先后顺序不同,我们这里以前序遍历为例子

1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树
2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树中(间)
3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树

  • 程序由上到下运行,应当先访问当前值,后访问左右结点。
    在这里插入图片描述

输出下图中的二叉树使用前序遍历的结果(结果需要包含NULL)

在这里插入图片描述

  • 结果为:1,2,3,NULL,NULL,NULL,4,5,NULL,NULL,6,NULL,NULL

我们画图分析,使用微元法每一个结点都逻辑细分一棵小树(根结点与左右结点),在开始根节点1时,对应的左结点2和右结点4
在这里插入图片描述

  • 之后先输出根结点的值1
  • 后访问左结点

使用微元法2结点微元成一棵小树
在这里插入图片描述

  • 之后先输出根结点的值2,此时结果为:1,2
  • 后访问左结点

使用微元法循环上述操作
在这里插入图片描述

  • 之后先输出根结点的值3,此时结果为:1,2,3
  • 后访问左结点

此时3的左节点使用微元法循环上述操作后可得下图
在这里插入图片描述

  • 之后先输出根结点的值NULL,此时结果为:1,2,3,NULL
  • 之后因为结点为NULL,所以结束访问,返回到结点3

此时结点3中,已经完成完成了左结点的范围,写在就需要范围右结点了
在这里插入图片描述
我们依旧使用微元法细分结点3的右结点,可得下图
在这里插入图片描述

  • 之后先输出根结点的值NULL,此时结果为:1,2,3,NULL,NULL
  • 之后因为结点为NULL,所以结束访问,返回到结点3

此时结点3的所以结点已经完成遍历,返回到结点2,结点2已经完成当前结点与左结点值的遍历,接下来进入右结点值遍历中
在这里插入图片描述
使用微元法循环上述操作,直到整棵树的值遍历一便即可得到结果:1,2,3,NULL,NULL,NULL,4,5,NULL,NULL,6,NULL,NULL

上面我们手搓的思想与递归简直一毛一样,那么我们在代码实现中,就可以使用递归来解决前序遍历。

根据我们手搓的画图,最终我们可以得出下图:

在这里插入图片描述

前序遍历的代码实现

递归的思想就是大事化小,对于递归方面有问题的小伙伴可以看不才写的递归笔记:【C语言】函数递归

代码实现:

void PreOrder(BTNode* root) {if (root == NULL) {printf("NULL ");return;}printf("%d ", root->val);PreOrder(root->left);PreOrder(root->right);
}

根据上面手搓推理的逻辑,继续使用微元法来创建递归实现前序遍历
在这里插入图片描述
在上图这颗微元树中

  • 首先需要判断这个树是否是空树,判断是否空树就是判断这个结点是否为NULL,如果结点是NULL就打印NULL并且返回。
  • 判断这个结点不为空后,因为是前序遍历,这时候就先打印根结点的值root->val
  • 打印完成后就访问左孩子结点,之后再访问右孩子结点,完成。
  • 在判断是否空树的条件中,恰好判断了结点为空的情况,这样也完成了我们访问左孩子或右孩子遇到结点为NULL需要返回的情况。

内存中的栈增情况:
在这里插入图片描述

中序遍历的代码实现

前序遍历的实现逻辑一模一样,只是执行根结点值打印时机不同

void InOrder(BTNode* root) {if (root == NULL) {printf("NULL ");return;}InOrder(root->left);printf("%d ", root->val);InOrder(root->right);
}
后序遍历的代码实现

前序遍历的实现逻辑一模一样,只是执行根结点值打印时机不同

void PostOrder(BTNode* root) {if (root == NULL) {printf("NULL ");return;}PostOrder(root->left);PostOrder(root->right);printf("%d ", root->val);
}

三、链式二叉树的元素个数

求链式二叉树的元素个数,我们还是先使用微元法来手搓计算。
在这里插入图片描述

计算下面二叉树有多少个元素

在这里插入图片描述
我们使用微元法先拆分一颗小树来分析如何计算在这里插入图片描述
首先根节点不为NULL说明该根结点是有效结点,但是在这个结点中不能得到左孩子与右孩子右多少个元素个数。让左右孩子返回它有多少个元素结点。这样在这个小树中,我们才能返回这棵树有多少个结点
所以又需要进入左右孩子中,去判断它是否是有多少个结点(如下图)
在这里插入图片描述
此时,我们还是不知道左右孩子各有多少个结点,我们再进入到左右孩子中(如下图)。
在这里插入图片描述
此时,我们还是不知道左右孩子各有多少个结点,我们再进入到左右孩子中。此时3的左节点使用微元法循环上述操作后可得下图
在这里插入图片描述
这时结点为NULL,说明该结点不是有效结点,返回0
在这里插入图片描述
此时3结点就得到了左孩子结点的值为0,这时就需要进入右孩子中,让右孩子返回元素个数

同理,右孩子还是空,返回0,此时3结点得知左右孩子节点个数。在这里插入图片描述

3节点左孩子返回结果 + 右孩子返回结果 再加上自身是有效结点+1就得到该结点的元素个数,左右元素个数为03节点是有效节点,所以返回1
在这里插入图片描述
节点2中就得到了左节点的元素个数,此时在按照上面的逻辑得到右节点个数为0(如下图)
在这里插入图片描述2节点中左元素个数为1,右元素个数为02节点是有效节点,所以返回2(如下图)在这里插入图片描述

根据上面的逻辑,我们可以轻松算出1结点右子树元素个树为3
在这里插入图片描述

此时1结点中左孩子返回结果为2 右孩子返回结果为3 再加上自身是有效结点+1就得到该结点的元素个数为:6

虽然也是遍历求解,但是我们这里是让每个底层结点自己统计完成后,逐步向上层返回结点个数

代码实现
int BinaryTreeSize(BTNode* root) {if (root == NULL) {return 0;}return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}
  • 如果是空结点就返回0
  • 使用微元法左右结点自己返回有多少结点,再加上自身结点就是该小树的元素个数

四、链式二叉树的高度

求链式二叉树的高度,还是使用微元法,逻辑与求个数相似。
在这里插入图片描述

手搓下面二叉树的高度

在这里插入图片描述
我们使用微元法先拆分一颗小树来分析如何计算在这里插入图片描述
在上图小树中,我们知道根节点不是空结点,这时候需要判断我们需要判断左右结点哪个孩子的高度高,之后我们在最高的子结点高度加上根结点的高度(+1)即可得到这个小树的高度,我们直接微元到原二叉树左子树的最底层中(如下图)在这里插入图片描述
在上图中,左右孩子的高度是0,但次结点时有效结点,所以返回结果是:0+1。(最高的子结点高度加上根结点),

此时上层结点2就接收到左结点的高度是1,易得右结点的高度是0在这里插入图片描述
之后最高的子结点高度(1)加上根结点的高度(1)返回2(如下图)
在这里插入图片描述
根据上述逻辑,我们易的。1结点的右孩子返回高度是2(如下图)
在这里插入图片描述
这时两个孩子的高度都为2,所以该二叉树的高度为:2 + 1 = 3

代码实现
int TreeHeight(BTNode* root) {if (root == NULL) {return 0;}int leftHeight = TreeHeight(root->left);int rightHeight = TreeHeight(root->right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
  • 在递归中,返回的数据需要进行处理的返回值都必须进行保存,这样可以避免大量重复的递归。
  • 每一次都需要保存左右结点的高度,这样就不需要多次重复递归即可判断出左孩子与右孩子哪个高度高

五、二叉树第k层节点个数

同样使用微元法求出第K层中的节点个数。
在这里插入图片描述

手搓下面二叉树中第3层的高度

在这里插入图片描述
首先,我们需要有一个变量i来记录我们当前所在的层次是否在所对于的K层中,我们使用微元法先拆分一颗小树来分析如何计算在这里插入图片描述
每进入一层,i变量就--,直到i变量为1时,说明当前的层次是正确的。但此时i == 3,这时候就需要进入根节点的左右孩子中,让左右孩子告诉根节点i==1是有多少个节点
在这里插入图片描述
首先进入到左孩子中,此时i==2,还是没进入到对于的节点,再次进入左右孩子中,此时发现i==1,这时候左孩子3是有效节点,返回1右孩子NULL不是有效节点。在这里插入图片描述
这时候节点2就需要把左右孩子返回的数据相加后返回
在这里插入图片描述
同理可得出右子树返回结果为:2
在这里插入图片描述
即的出第3层的元素个数为:3

代码实现
int TreeKLeve(BTNode* root,int k) {if (root == NULL) {//节点为空时,返回0return 0;}if (k == 1) { //节点不为空且层次为1时,返回1return 1;}int lnum = TreeKLeve(root->left, k - 1);//记录左孩子在K层时有多少个节点int rnum = TreeKLeve(root->right, k - 1);//记录右孩子在K层时有多少个节点return lnum + rnum;//返回左孩子在K层中节点 + 右孩子在K层中节点。即为当前根下K层中所有节点个数
} 
  • 在递归中,返回的数据需要进行处理的返回值都必须进行保存,这样可以避免大量重复的递归。

六、二叉树查找值为x的节点

相对简单,但是代码逻辑中会有坑
在这里插入图片描述

在下面二叉树中,找到值为5的节点,并且返回该节点

在这里插入图片描述
只需要遍历找到值为5的节点,之后返回即可,逻辑与求元素个数大差不差。

代码实现
BTNode* BinaryTreeFind(BTNode* root, BTDataType x) {if (root == NULL) {return NULL;}if (root->val == x) {return root;}BTNode* lren = BinaryTreeFind(root->left,x);if (lren)return lren;BTNode* rren = BinaryTreeFind(root->right,x);if (rren)return rren;return NULL;
}
  • 代码实现逻辑不断的遍历往下走,直到为空返回空,或者找到对应的值返回该节点。
  • 如果既不为空也不是相对应的值,则接着往左右孩子中寻找X值。
  • 如果左右孩子中都没有找到所对应的X值,则返回空值。
  • 若找到了所对应的X值。则返回对应的节点。因为递归是函数的栈增return不能直接返回到函数调用的阶段需要一步步返回
  • 为了确保每次返回都是所对应的节点,我们需要进行判断每次返回的节点是否为空。如果不为空,则返回所找到的节点。
  • 一旦涉及到需要对递归值进行判断的,都需要进行储存。

如果不进行判断返回,则在下一次函数递归返回中就直接返回空。(错误案例)

BTNode* BinaryTreeFind(BTNode* root, BTDataType x) {if (root == NULL) {return NULL;}if (root->val == x) {return root;}return NULL;
}

七、链式存储二叉树的OJ

7.1、单值二叉树

如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树

只有给定的树是单值二叉树时,才返回 true;否则返回 false
在这里插入图片描述

输入:[1,1,1,1,1,null,1]
输出:true

本题不可使用遍历。这里我们使用微元法判断一下我们这么确认每个节点都是相同的

在这里插入图片描述
使用根节点val左右孩子的val值分别比较,即可判断出这个节点是否与左右孩子的值相同。
但是我们也不知道左右孩子的全部节点是否相同,但是如果此时不满住条件我们直接可以判断为假,所以我们先对该节点的左右孩子进行判断
在这里插入图片描述
左节点判断结果不为假,继续判断右结点
在这里插入图片描述
此时右结点也不为假,所以我们需要递归往下判断,左右孩子的节点是否为假。如此递归,当遇到节点为NULL时,返回true,则说明到叶子节点都不为假,一旦为假,直接返回。

代码实现
bool isUnivalTree(struct TreeNode* root) {if (root == NULL) {  // 基本情况:如果当前节点为空,返回 true(空树被认为是单值树)return true;}// 检查左子节点是否存在且值不同if (root->left != NULL && root->val != root->left->val) {return false;}// 检查右子节点是否存在且值不同if (root->right != NULL && root->val != root->right->val) {return false;}// 递归检查左子树和右子树return isUnivalTree(root->left) && isUnivalTree(root->right);
}

相似的逻辑,我们试试下面的题目:

  • 100. 相同的树
  • 101. 对称二叉树
  • 572. 另一棵树的子树

八、二叉树的创建和销毁

通过前序遍历的数组:1,2,3,NULL,NULL,NULL,4,5,NULL,NULL,6,NULL,NULL。构建二叉树

首先,我们需要把前序遍历的数组还原为二叉树,还原过程:
在这里插入图片描述
由上图可知,前序遍历的数组的还原过程,在数组下标+1我们就进行左孩子的赋值,当左孩子赋值NULL后,程序就返回节点,之后进行右结点的赋值,当右节点也赋值完成后,返回节点地址。

代码实现
typedef char BTDataType;
typedef struct BinaryTreeNode
{struct BinTreeNode* left;// 指向当前节点左孩子struct BinTreeNode* right; // 指向当前节点右孩子BTDataType val; // 当前节点值域}BTNode;
//前序递归创建二叉树
BTNode* CreateTree(char* arr, int* pi) {if (arr[*pi] == '#') {(*pi)++;return NULL;}BTNode* node = (BTNode*)malloc(sizeof(BTNode));node->val = arr[(*pi)++];node->left = CreateTree(arr, pi);node->right = CreateTree(arr, pi);return node;
}void test2() {char arr[] = { '1','2','3','#','#','#','4','5','#','#','6','#','#' };//'#'代表NULLint i = 0;BTNode* node = CreateTree(arr, &i);
}

我们画部分的递归展开图以便理解:
在这里插入图片描述


以上就是本章所有内容。若有勘误请私信不才。万分感激💖💖 如果对大家有帮助的话,就请多多为我点赞收藏吧~~~💖💖
请添加图片描述

ps:表情包来自网络,侵删🌹

相关文章:

【数据结构】树——链式存储二叉树的基础

写在前面 书接上文:【数据结构】树——顺序存储二叉树 本篇笔记主要讲解链式存储二叉树的主要思想、如何访问每个结点、结点之间的关联、如何递归查找每个结点,为后续更高级的树形结构打下基础。不了解树的小伙伴可以查看上文 文章目录 写在前面 一、链…...

STM32-- keil常见报错与解决办法

调试问题 1. keil在线调试需要点击好几次运行才可以运行,要是直接下载程序直接就不运行。 解决:target里面的use microlib要勾选,因为使用了printf。 keil在线调试STM32,点三次运行才能跑到main的问题解决。 keil在线调试STM32…...

【大数据学习 | Spark-Core】RDD的概念与Spark任务的执行流程

1. RDD的设计背景 在实际应用中,存在许多迭代式计算,这些应用场景的共同之处是,不同计算阶段之间会重用中间结果,即一个阶段的输出结果会作为下一个阶段的输入。但是,目前的MapReduce框架都是把中间结果写入到HDFS中&…...

一文读懂埋阻埋容工艺

PCB 埋阻埋容工艺是一种在 PCB 板内部埋入电阻和电容的工艺。通常情况下, PCB 上电阻和电容都是通过贴片技术直接焊接在板面上的,而埋阻埋容工艺则将电 阻和电容嵌入到 PCB 板的内部层中,这种印制电路板,其自下而上依次包括第一介电 层,隐埋电…...

mysql 数据表导出为 markdown(附 go 语言 gorm 的实际使用)

前言 通常业务系统开发中,数据库的设计与维护是至关重要的环节。而数据库的文档化则是确保团队成员之间有效沟通、快速理解系统架构的基础。 但目前数据文档都是手动写的,耗时费力,由于当前项目使用的是 mysql 作为存储引擎,找找…...

本地云存储 MinIO 中修改用户密码

本地云存储 MinIO 中修改用户密码 MinIO 中修改用户密码前提条件步骤 1:安装 MinIO Client对于 Linux/macOS:对于 Windows: 步骤 2:配置 MinIO Client步骤 3:查看现有用户步骤 4:修改用户密码步骤 5&#x…...

go项目中比较好的实践方案

工作两年来,我并未遇到太大的挑战,也没有特别值得夸耀的项目。尽管如此,在日常的杂项工作中,我积累了不少心得,许多实践方法也在思考中逐渐得到优化。因此,我在这里记录下这些心得。 转发与封装 这个需求…...

回溯法基础入门解析

回溯法 前 言 回溯法也可以叫做回溯搜索法,它是一种搜索的方式。回溯是递归的副产品,只要有递归就会有回溯。回溯法,一般可以解决如下几种问题: 组合问题:N个数里面按一定规则找出k个数的集合切割问题:一…...

计算机网络-VPN虚拟专用网络概述

前面我们学习了在企业内部的二层交换机网络、三层路由网络包括静态路由、OSPF、IS-IS、NAT等,现在开始学习下VPN(Virtual Private Network,虚拟专用网络),其实VPN可能很多人听到第一反应就是梯子,但是其实这…...

信创时代的数据库之路:2024 Top10 国产数据库迁移与同步指南

数据库一直是企业数字化和创新的重要基础设施之一。从传统的关系型数据库到非关系型数据库、分析型数据库,再到云数据库和多模数据库,这一领域仍在持续变革中,各种新型数据库产品涌现,数据管理的能力和应用场景也由此得到了扩展。…...

自制游戏:监狱逃亡

第一个游戏&#xff0c;不喜勿喷&#xff1a; #include<bits/stdc.h> #include<windows.h> using namespace std; int xz; int ruond_1(int n){if(xz1){printf("撬开了&#xff0c;但站在你面前的是俄罗斯内务部特种部队的奥摩大帝&#xff0c;你被九把加特…...

小雪时节,阴盛阳衰,注意禁忌

宋张嵲《小雪作》 霜风一夜落寒林&#xff0c;莽苍云烟结岁阴。 把镜渐无勋业念&#xff0c;爱山唯驻隐沦心。 冰花散落衡门静&#xff0c;黄叶飘零一迳深。 世乱身穷无可奈&#xff0c;强将悲慨事微吟。 网络图片&#xff1a;小雪时节 笔者禁不住喟然而叹&#xff1a;“冰…...

CPU性能优化--微操作

x86 架构处理器吧复杂的CISC指令转为简单的RISC微操作。这样做最大的优势是微操作可以乱序执行&#xff0c;一条简单的相加指令--比如ADD&#xff0c;EAX, EBX&#xff0c;只产生一个微操作&#xff0c;而很多复杂指令--比如ADD, EAX 可能会产生两个微操作&#xff0c;一个将数…...

工厂模式

主要解决对象的创建问题 首先是简单工厂 只有一个工厂类&#xff0c;每次有新的产品就需要修改里面接口的内容&#xff0c;违反了封闭原则 //1、定义抽象产品类 class AbstractCar { public:AbstractCar() default;virtual ~AbstractCar() default;virtual void showName(…...

嵌入式系统与OpenCV

目录 一、OpenCV 简介 二、嵌入式 OpenCV 的安装方法 1. Ubuntu 系统下的安装 2. 嵌入式 ARM 系统中的安装 3. Windows10 和树莓派系统下的安装 三、嵌入式 OpenCV 的性能优化 1. 介绍嵌入式平台上对 OpenCV 进行优化的必要性。 2. 利用嵌入式开发工具&#xff0c;如优…...

编程之路,从0开始:动态内存笔试题分析

Hello大家好&#xff0c;很高兴我们又见面啦&#xff01; 给生活添点passion&#xff0c;开始今天的编程之路。 今天我们来看几个经典的动态内存笔试题。 1、题目1 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<string.h> void GetMemory(char* …...

物联网研究实训室建设方案

一、引言 随着物联网技术的快速发展&#xff0c;其在各个行业的应用越来越广泛&#xff0c;对物联网专业人才的需求也日益增加。为满足这一需求&#xff0c;建设一个符合现代化教学需求的物联网研究实训室&#xff0c;对于提高学生的实践能力和创新能力具有重要意义。本方案旨…...

Mac vscode 激活列编辑模式

列编辑模式在批量处理多行文本时&#xff0c;非常有效&#xff0c;但 vscode 默认情况下&#xff0c;又没有激活&#xff0c;因此记录一下启动方法&#xff1a; 激活列编辑模式 然后就可以使用 Alt&#xff08;Mac 上是 Option 或 Command 键&#xff09; 鼠标左键 滑动选择了…...

深度学习:GPT-1的MindSpore实践

GPT-1简介 GPT-1&#xff08;Generative Pre-trained Transformer&#xff09;是2018年由Open AI提出的一个结合预训练和微调的用于解决文本理解和文本生成任务的模型。它的基础是Transformer架构&#xff0c;具有如下创新点&#xff1a; NLP领域的迁移学习&#xff1a;通过最…...

前端图像处理(一)

目录 一、上传 1.1、图片转base64 二、图片样式 2.1、图片边框【border-image】 三、Canvas 3.1、把canvas图片上传到服务器 3.2、在canvas中绘制和拖动矩形 3.3、图片(同色区域)点击变色 一、上传 1.1、图片转base64 传统上传&#xff1a; 客户端选择图片&#xf…...

unity中:超低入门级显卡、集显(功耗30W以下)运行unity URP管线输出的webgl程序有那些地方可以大幅优化帧率

删除Global Volume&#xff1a; 删除Global Volume是一项简单且高效的优化措施。实测表明&#xff0c;这一改动可以显著提升帧率&#xff0c;甚至能够将原本无法流畅运行的场景变得可用。 更改前的效果&#xff1a; 更改后的效果&#xff1a; 优化阴影和材质&#xff1a; …...

ftdi_sio应用学习笔记 4 - I2C

目录 1. 查找设备 2. 打开设备 3. 写数据 4. 读数据 5. 设置频率 6 验证 6.1 遍历设备 6.2 开关设备 6.3 读写测试 I2C设备最多有6个&#xff08;FT232H&#xff09;&#xff0c;其他为2个。和之前的设备一样&#xff0c;定义个I2C结构体记录找到的设备。 #define FT…...

如何更好的把控软件测试质量

如何更好的把控软件测试质量 在软件开发过程中&#xff0c;测试是确保软件质量、稳定性和用户体验的重要环节。随着需求的不断变化以及技术的不断进步&#xff0c;如何更好的把控软件测试质量已成为一个不可忽视的话题。本文将从几个维度探讨确保软件质量的方法和方案&#xf…...

“漫步北京”小程序及“气象景观数字化服务平台”上线啦

随着科技的飞速发展&#xff0c;智慧旅游已成为现代旅游业的重要趋势。近日&#xff0c;北京万云科技有限公司联合北京市气象服务中心&#xff0c;打造的“气象景观数字化服务平台“和“漫步北京“小程序已经上线&#xff0c;作为智慧旅游的典型代表&#xff0c;以其丰富的功能…...

SOL链上的 Meme 生态发展:从文化到创新的融合#dapp开发#

一、引言 随着区块链技术的不断发展&#xff0c;Meme 文化在去中心化领域逐渐崭露头角。从 Dogecoin 到 Shiba Inu&#xff0c;再到更多细分的 Meme 项目&#xff0c;这类基于网络文化的加密货币因其幽默和社区驱动力吸引了广泛关注。作为近年来备受瞩目的区块链平台之一&…...

身份证实名认证API接口助力电商购物安全

亲爱的网购达人们&#xff0c;你们是否曾经因为网络上的虚假信息和诈骗而感到困扰&#xff1f;在享受便捷的网购乐趣时&#xff0c;如何确保交易安全成为了我们共同关注的话题。今天&#xff0c;一起来了解一下翔云身份证实名认证接口如何为电子商务保驾护航&#xff0c;让您的…...

【过程控制系统】第6章 串级控制系统

目录 6. l 串级控制系统的概念 6.1.2 串级控制系统的组成 6.l.3 串级控制系统的工作过程 6.2 串级控制系统的分析 6.2.1 增强系统的抗干扰能力 6.2.2 改善对象的动态特性 6.2.3 对负荷变化有一定的自适应能力 6.3 串级控制系统的设计 6.3.1 副回路的选择 2.串级系…...

YOLOv11融合针对小目标FFCA-YOPLO中的FEM模块及相关改进思路

YOLOv11v10v8使用教程&#xff1a; YOLOv11入门到入土使用教程 YOLOv11改进汇总贴&#xff1a;YOLOv11及自研模型更新汇总 《FFCA-YOLO for Small Object Detection in Remote Sensing Images》 一、 模块介绍 论文链接&#xff1a;https://ieeexplore.ieee.org/document/10…...

qt+opengl 三维物体加入摄像机

1 在前几期的文章中&#xff0c;我们已经实现了三维正方体的显示了&#xff0c;那我们来实现让物体的由远及近&#xff0c;和由近及远。这里我们需要了解一个概念摄像机。 1.1 摄像机定义&#xff1a;在世界空间中位置、观察方向、指向右侧向量、指向上方的向量。如下图所示: …...

day05(单片机高级)PCB基础

目录 PCB基础 什么是PCB&#xff1f;PCB的作用&#xff1f; PCB的制作过程 PCB板的层数 PCB设计软件 安装立创EDA PCB基础 什么是PCB&#xff1f;PCB的作用&#xff1f; PCB&#xff08;Printed Circuit Board&#xff09;&#xff0c;中文名称为印制电路板&#xff0c;又称印刷…...