【数据结构取经之路】图解AVL树

目录

AVL树的前世今生

走进AVL

AVL树的实现

1、AVL树节点的定义 

2、AVL树的插入

3、完整代码 

AVL树的性能


AVL树的前世今生

我们知道,普通的二叉搜索树在不少场景下可以提高我们的查找效率。例如下面这种情况,我们要找22。从根开始,一比较就直接排除了将近一半的数据,实在是快活呀!

但是先别高兴的太早,如果不出意外的话,意外就要发生了。在数据有序或者接近有序的场景下,它就招架不住了。请看:

这时二叉搜索树已经退化为了单支树,查找的效率就相当于在顺序表中搜索元素了。结构比顺序表复杂,而且效率还和顺序表一样,你说,我要你有何用?!所以,二叉搜索树呀,虽然你在不少场景下表现的很好,但是还不够! 这时普通的二叉搜索树急需改进。按照剧情发展,每当到这种关键时刻,一定会有能人站出来“挽狂澜于既倒,扶大厦之将倾”。没错,俄罗斯的两位数学家站了出来。

走进AVL

书接上回。1962年,俄罗斯的两位数学家G.M. Adelson-Velsky和E.M. Landis提出了一种解决普通二叉搜索树退化的方案:

当向二叉搜索树中插入节点后,如果能保证每个节点的左右子树高度之差的绝对值不超过1,那么就可以降低树的高度,从而减少平均搜索长度。

有人也许会有这样的疑问:为什么不保证每个节点的左右子树高度之差为0呢?我猜当年俄罗斯的那两位数学家也想过这个问题,他们既然考虑到了这一点,为何这两位大佬不把AVL树设计成左右子树高度差为0呢?是他们水平不行?!那还真不是。你想想,如果这棵树只有2个节点、4个节点,那么根本无法做到绝对平衡。

一棵AVL树,要么是空树,要么是具有以下性质的二叉搜索树:

        ● 它的左右子树都是AVL树。

        ● 左右子树的高度之差(平衡因子)的绝对值不超过1。

上面引出了一个叫平衡因子的概念。那么,我的问题来了,什么是平衡因子?为什么要有平衡因子?平衡因子是不是必须的?

首先,解释第一个问题——什么是平衡因子。所谓平衡因子,是针对某个节点来说的,平衡因子的值就是该节点右子树高度 - 左子树高度(或者左子树高度 - 右子树高度)的绝对值。说明一下:下面对于平衡因子的计算,全部采用右子树 - 左子树。  

例如上图中的值为2的结点,它的右子树高度为0,左子树高度为1,所以它的平衡因子为-1。 

其次,我们来谈一谈为什么要有平衡因子。平衡因子存在的意义就是为了帮助我们判断本次插入是否需要旋转,以确保树的平衡。

例如,新插入的值为0的结点,通过平衡因子的更新,我们可以知道此时需要进行旋转操作了(当某个节点的平衡因子的绝对值超过1时,就需要进行旋转了)。

最后,我们来谈谈平衡因子是不是必须的。并不是的,我们已经知道了平衡因子存在的意义——帮助我们判断是否需要旋转以确保树的平衡。不用平衡因子也可以实现。例如,我们在插入节点0时,通过栈等数据结构保存父节点,通过父节点计算出右子树的高度等方法也是可以的,只是相对比较麻烦。说明一下:本文是需要引入平衡因子(Balance Factor)的。

一棵具有n个节点的AVL树其高度可以保持在O(logn)(以2为底),搜素的时间复杂度也就是高度次O(logn)

AVL树的概念及其基本性质我们已经了解了,下面我们开始着手实现,练一练手上的功夫。

AVL树的实现

1、AVL树节点的定义 

        ● 指向左孩子和右孩子的指针。

        ● 记录该节点平衡因子的bf。

        ● 指向父节点的指针。

前两点我相信大家都没有疑问,可能有疑问的点在于:为什么需要指向父节点的指针?请看上图,假设值为-1的结点是新插入的结点,那么在插入之前值为1的结点的平衡因子为0。插入值为-1的结点之后,值为1的结点的平衡因子需要更新为-1。新插入的值为0的结点的左右指针均指向空,那么我们如何能找到新插入节点的父节点,然新它的平衡因子呢?这时引入指向父节点的指针就可以帮助我们简单快速的解决这个问题。通过该指针,我们很容易找到每一个节点的父节点,方便插入节点时修改父节点的平衡因子。

template <class T>
struct AVLTreeNode
{ALVTreeNode<T>* _left;ALVTreeNode<T>* _right;ALVTreeNode<T>* _parent;int _bf;//平衡因子T _data;//结点中存储的数据AVLTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr),_bf(0), _data(data){}
};

2、AVL树的插入

AVL树本质上还是一棵二叉搜索树,所以无论何种情况,它都必须遵循二叉搜索树的插入规则。再加上AVL树本身特有的平衡因子,我们只需要把它也维护起来即可。下面总结一下:

        ● 按照二叉搜索树的方式插入新的结点。

        ● 更新结点的平衡因子。

这两步无论在何种情况下插入都是不可避免的,至于不同情况下的特殊处理,我们接下来慢慢谈。

对于平衡因子,这里采用的是右-左的规则,所以如果在左子树插入,对应的父节点的平衡因子要减1。反之,如果在右子树插入,对应的父节点的平衡因子加1。

插入时的4种情况

①左左: 在左子树根节点(值为30的结点)的左子树上插入节点,进行右单旋。

右单旋的操作就是把subL给提起来,parent自然就会下沉。这样一来,左子树的高度降低,右子树的高度增加,扭转了不平衡的局面。subL变成了根节点,parent变成了subL的右子树,至于subLR,原来是subL的右子树,同时也是parent左子树中的一员,说明subLR比subL大,比parent小,所以将它挂在parent的左子树就很合适。 我们可以看到,在旋转前后,subLR的左右子树没有任何变化,所以subLR的平衡因子在旋转前后不变,parent和subL的平衡因子变为0。当然,这其中还隐藏着一些细节,这些细节都体现在了代码里。

void RotateR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;//将subLR挂在parent的左子树上parent->_left = subLR;//如果subLR不为空,则更新它的父节点指针if (subLR)			  subLR->_parent = parent;//更新subL的右孩子subL->_right = parent;//在更新parent的父节点指针前记录它原来的指向Node* parentParent = parent->_parent;//更新parent的父节点指针parent->_parent = subL;//更新subL的父节点指针subL->_parent = parentParent;//如果parentParent不为空,subL在左子树还是右//子树由parent决定,因为subL是来代替parent的if (parentParent){if (parentParent->_left == parent)parentParent->_left = subL;elseparentParent->_right = subL;}else{//parentParent为空,说明旋转前//parent是根,旋转后要更新根结点_root = subL;subL->_parent = nullptr;}//更新平衡因子subL->_bf = parent->_bf = 0;
}

②右右:在右子树根节点(值为60的结点)的右子树上插入,左单旋。

 

左单旋和右单旋是对称的,当我们把subR提起来时,parent就会下沉,进而左子树的高度增加,右子树的高度下降,维持平衡。 具体细节还是在代码里体现更直观一些。左单旋和右单旋这一组,认真理解其中一个后,另一个自然就理解了。

//左单旋的思路和右单旋是一样的,这里我就不过多解释了
void RotateL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;subR->_left = parent;Node* parentParent = parent->_parent;parent->_parent = subR;subR->_parent = parentParent;if (parentParent){if (parentParent->_left == parent)parentParent->_left = subR;elseparnetParent->_right = subR;}else{_root = subR;subR->_parent = nullptr;}subR->_bf = parent->_bf = 0;
}

③在左子树的根节点(值为30的节点)的右子树上插入新节点引起的不平衡,左右双旋,即先左旋再右旋。

 

左旋:对以subL为根的子树进行左单旋。

右旋:对以parent为根的子树进行右单旋。

这里一定要记住每一次旋转的时候是以谁为根结点的。如果把第一次的左旋理解为对以parent为根的子树进行左旋,那就错了。至于左单旋和右单旋如何操作,我这里就不赘述了,相信看到这里的你一定把前面的单旋都理解了。这里重点关注的对象是平衡因子。

在左子树的根节点(值为30的节点)的右子树上插入新节点引起不平衡的3中情形:

        ● 当h == 0,60本身就是新增节点。 

        ● 当 h > 0,在b子树插入新增节点引发不平衡。

        ● 当 h > 0,在c子树插入新增节点引发不平衡。

这三种情形对应subLR的平衡因子有三种情况:0,-1,1。

旋转前subLR的bf旋转后parent的bf旋转subL的bf旋转后subLR的bf
0000
-1100
10-10

通过这张表,我们看到,每一行的和都为0,这个规律也许能帮我们在不画图的情况下完成bf的调整。

void RotateLR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;//后面根据旋转前subLR的平衡因子来调整,需要记录RotateL(subL);RotateR(parent);if (bf == 0){parent->_bf = 0;subL->_bf = 0;subLR->_bf = 0;}else if (bf == -1){parent->_bf = 1;subL->_bf = 0;subLR->_bf = 0}else if (bf == 1){parent->_bf = 0;subL->_bf = -1;subLR->_bf = 0}else{assert(false);}
}

 ④在右子树的根节点(值为90的节点)的左子树上插入新节点引起的不平衡,右左双旋,即先右旋再左旋。

右左双旋和左右双旋是一对的,分析方法完全一样。 

右旋:对以subR为根的子树进行右单旋。

左旋:对以parent为根的子树进行左单旋。

在右子树的根节点(值为90的节点)的左子树上插入新节点引起不平衡的3中情形:

        ● 当h == 0,60本身就是新增节点。 

        ● 当 h > 0,在b子树插入新增节点引发不平衡。

        ● 当 h > 0,在c子树插入新增节点引发不平衡。

这三种情形对应subRL的平衡因子有三种情况:0,-1,1。

旋转前subRL的bf旋转后parent的bf旋转subR的bf旋转后subLR的bf
0000
-1010
1-100
void RotateRL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(subR);RotateL(parent);if (bf == 0){parent->_bf = 0;subR->_bf = 0;subRL->_bf = 0;}else if (bf == -1){parent->_bf = 0;subR->_bf = 1;subRL->_bf = 0;}else if (bf == 1){parent->_bf = -1;subR->_bf = 0;subRL->_bf = 0;}else{assert(false);}
}

有了前面的基础,下面我们来实现insert。

bool Insert(const T& data)
{//首次插入if (_root == nullptr){_root = new Node(data);return true;}Node* cur = _root;Node* parent = nullptr;//按二叉搜索树的方法插入,先找到位置while (cur){if (cur->_data < data){parent = cur;cur = cur->_right;}else if (cur->_data > data){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(data);//插入if (cur->_data < data)parent->_right = cur;elseparent->_left = cur;cur->_parent = parent;//调整平衡因子while (parent){if (parent->_left == cur)parent->_bf--;elseparent->_bf++;//如果插入后bf==0,则不需要继续向上调整//因为没有改变所在子树的高度if (parent->_bf == 0)break;//改变了所在子树的高度,继续向上调整else if (parent->_bf == 1 || parent->_bf == -1){cur = parent;parent = cur->_parent;}//需要旋转处理else if (parent->_bf == 2 || parent->_bf == -2){if (parent->_bf == -2 && cur->_bf == -1)RotateR(parent);else if (parent->_bf == 2 && cur->_bf == 1)RotateL(parent);else if (parent->_bf == -2 && cur->_bf == 1)RotateLR(parent);elseRotateRL(parent);}else{assert(false);}}return true;
}

3、完整代码 

 

#pragma once
#include <assert.h>
#include <iostream>
using namespace std;template <class T>
struct AVLTreeNode
{AVLTreeNode<T>* _left;AVLTreeNode<T>* _right;AVLTreeNode<T>* _parent;int _bf;//平衡因子T _data;//结点中存储的数据AVLTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr),_bf(0), _data(data){}
};template <class T>
class AVLTree
{typedef AVLTreeNode<T> Node;
public:bool Insert(const T& data){//首次插入if (_root == nullptr){_root = new Node(data);return true;}Node* cur = _root;Node* parent = nullptr;//按二叉搜索树的方法插入,先找到位置while (cur){if (cur->_data < data){parent = cur;cur = cur->_right;}else if (cur->_data > data){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(data);//插入if (parent->_data < data)parent->_right = cur;elseparent->_left = cur;cur->_parent = parent;//调整平衡因子while (parent){if (parent->_left == cur)parent->_bf--;elseparent->_bf++;//如果插入后bf==0,则不需要继续向上调整//因为没有改变所在子树的高度if (parent->_bf == 0)break;//改变了所在子树的高度,继续向上调整else if (parent->_bf == 1 || parent->_bf == -1){cur = parent;parent = cur->_parent;}//需要旋转处理else if (parent->_bf == 2 || parent->_bf == -2){if (parent->_bf == -2 && cur->_bf == -1)RotateR(parent);else if (parent->_bf == 2 && cur->_bf == 1)RotateL(parent);else if (parent->_bf == -2 && cur->_bf == 1)RotateLR(parent);elseRotateRL(parent);}else{assert(false);}}return true;}void Inorder() { _Inorder(_root); }
private:void _Inorder(Node* root){if (root == nullptr)return;_Inorder(root->_left);cout << root->_data << " ";_Inorder(root->_right);}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;//将subLR挂在parent的左子树上parent->_left = subLR;//如果subLR不为空,则更新它的父节点指针if (subLR)			  subLR->_parent = parent;//更新subL的右孩子subL->_right = parent;//在更新parent的父节点指针前记录它原来的指向Node* parentParent = parent->_parent;//更新parent的父节点指针parent->_parent = subL;//更新subL的父节点指针subL->_parent = parentParent;//如果parentParent不为空,subL在左子树还是右//子树由parent决定,因为subL是来代替parent的if (parentParent){if (parentParent->_left == parent)parentParent->_left = subL;elseparentParent->_right = subL;}else{//parentParent为空,说明旋转前//parent是根,旋转后要更新根结点_root = subL;subL->_parent = nullptr;}//更新平衡因子subL->_bf = parent->_bf = 0;}//左单旋的思路和右单旋是一样的,这里我就不过多解释了void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;subR->_left = parent;Node* parentParent = parent->_parent;parent->_parent = subR;subR->_parent = parentParent;if (parentParent){if (parentParent->_left == parent)parentParent->_left = subR;elseparentParent->_right = subR;}else{_root = subR;subR->_parent = nullptr;}subR->_bf = parent->_bf = 0;}void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;//后面根据旋转前subLR的平衡因子来调整,需要记录RotateL(subL);RotateR(parent);if (bf == 0){parent->_bf = 0;subL->_bf = 0;subLR->_bf = 0;}else if (bf == -1){parent->_bf = 1;subL->_bf = 0;subLR->_bf = 0;}else if (bf == 1){parent->_bf = 0;subL->_bf = -1;subLR->_bf = 0;}else{assert(false);}}void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(subR);RotateL(parent);if (bf == 0){parent->_bf = 0;subR->_bf = 0;subRL->_bf = 0;}else if (bf == -1){parent->_bf = 0;subR->_bf = 1;subRL->_bf = 0;}else if (bf == 1){parent->_bf = -1;subR->_bf = 0;subRL->_bf = 0;}else{assert(false);}}
private:Node* _root = nullptr;
};#include "ALVTree.h"void Test()
{//int arr1[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };int arr1[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };AVLTree<int> t1;for (auto val : arr1){t1.Insert(val);}t1.Inorder();
}int main()
{Test();return 0;
}

AVL树的性能

由于 AVL 树是一棵平衡二叉搜索树,其每个节点的左右子树的高度差都不超过1,所以 AVL 树是无限接近于满二叉树的。因此AVL 进行查询的时间复杂度就为 O(logN),非常高效;但是如果要对 AVL 树做一些结构修改的操作,其性能就比较低;因为 AVL 树插入时需要调整其达到平衡,那么进行旋转的次数就比较多,更差的是在删除时,有可能要一直让旋转持续到根的位置;因此如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变) 或数据较少进行插入和删除,则可以考虑 AVL 树,但如果一个结构经常进行修改,AVL 则不太适合。


本文到这就结束啦,感谢你的支持! 

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

搭建一个基于角色的权限验证框架

说明&#xff1a;基于角色的权限验证&#xff08;Role-Based Access Control&#xff0c;RBAC&#xff09;框架&#xff0c;是目前大多数服务端的框架。本文介绍如何快速搭建一个这样的框架&#xff0c;不用Shiro、Spring Security、Sa-Token这样的“大框架”实现。 RBAC 基于…

[Meachines] [Medium] Bart Server Monitor+Internal Chat+UA投毒+Winlogon用户密码泄露权限提升

信息收集 IP AddressOpening Ports10.10.10.81TCP:80 $ nmap -p- 10.10.10.81 --min-rate 1000 -sC -sV PORT STATE SERVICE VERSION 80/tcp open http Microsoft IIS httpd 10.0 | http-methods: |_ Potentially risky methods: TRACE |_http-server-header: Micros…

在沉浸式翻译中使用SiliconCloud API:提升翻译体验

沉浸式翻译&#xff0c;作为广受好评的双语对照网页翻译插件&#xff0c;结合了硅基流动&#xff08;SiliconFlow&#xff09;的大语言模型&#xff0c;为用户提供了快速、准确的跨语言翻译服务。自2023年上线以来&#xff0c;这款AI双语对照网页翻译扩展已帮助超过100万用户跨…

【C++11 —— 异常】

C —— 异常 C语言传统的处理错误的方式C异常概念异常的使用异常的抛出和捕获异常的重新抛出异常安全异常规范 自定义异常体系自定义异常体系的目的 C标准库的异常体系异常的优缺点 C语言传统的处理错误的方式 在C语言中&#xff0c;错误处理通常依赖于返回值和全局变量的方式…

【程序人生】《把时间当做朋友》李笑来思维导图

李笑来&#xff0c;男&#xff0c;朝鲜族&#xff0c;1972年7月12日生&#xff0c;吉林人&#xff0c;畅销书作家、区块链专家、天使投资人。 INBlockchain硬币资本创始人&#xff0c;投资了区块链项目。曾在央视采访中自曝拥有六位数比特币。 出版发行《把时间当做朋友》 [9]、…

LAMP环境搭建记录:基于VM的Ubuntu虚拟机

LAMP环境搭建记录&#xff1a;基于VM的Ubuntu虚拟机 我们从这样的角度出发&#xff1a; 一、简述LAMP环境 二、搭建LAMP环境 一、什么是LAMP环境 首先&#xff0c;该词是复合&#xff1a; ​ LAMP Linux Apache MySQL PHP 其中&#xff0c;逐项简述为&#xff1a; …

代码随想录算法训练营第57天|卡码网 53. 寻宝 prim算法精讲和kruskal算法精讲

1. prim算法精讲 题目链接&#xff1a;https://kamacoder.com/problempage.php?pid1053 文章链接&#xff1a;https://www.programmercarl.com/kamacoder/0053.寻宝-prim.html prim算法 是从节点的角度 采用贪心的策略 每次寻找距离 最小生成树最近的节点 并加入到最小生成树中…

MoCo和SimCLR【CV双雄】

文章目录 文章列表五、MoCo V15.1 文章摘要5.2 实验结果5.3 文章图示图 1: Momentum Contrast (MoCo) 训练方法概述图 2: 三种对比损失机制的比较 六、SimCLR V16.1 文章摘要6.2 文章实验6.3 文章图示图 1: ImageNet Top-1 Accuracy of Linear Classifiers图 2: 对比学习框架图…

MySQL日志binlog和redo log区别

MySQL binlog简介 MySQL中有两类日志&#xff1a;binlog和redo log&#xff0c;分别有不同的作用和解决问题。binlog是归档日志&#xff0c;在MySQL server层的日志&#xff0c;适用于所有存储引擎&#xff0c;redo log是innodb特有日志用于crash-safe时恢复数据。 binlog和r…

【C++】—— list 模拟实现

【C】—— list 模拟实现 1 list 基础结构2 默认构造3 迭代器3.1 整体框架3.2 成员函数3.3 begin() 与 end() 的实现3.4 operator-> 的实现3.5 const 迭代器3.5.1 const 迭代器为什么命名 const_iterator3.5.2 const 迭代器的实现3.5.3 合并两个迭代器 4 源码 1 list 基础结…

【C++前后缀分解】1653. 使字符串平衡的最少删除次数|1793

前后缀分解 C前后缀分解 LeetCode1653. 使字符串平衡的最少删除次数 给你一个字符串 s &#xff0c;它仅包含字符 ‘a’ 和 b’​​​​ 。 你可以删除 s 中任意数目的字符&#xff0c;使得 s 平衡 。当不存在下标对 (i,j) 满足 i < j &#xff0c;且 s[i] ‘b’ 的同时…

软考中项(第三版):项目质量管理总结

前言 系统集成项目管理工程师考试&#xff08;简称软考中项&#xff09;&#xff0c;其中案例分析也是很大一部分考试内容&#xff0c;目前正在学习中&#xff0c;现总结一些可能会考到的知识点供大家参考。 1.1、项目质量管理总线索 1、质量管理的发展史 &#xff08;1&…

创建一个Java项目并在项目中新建文件,最后实现程序简单的输出

目录 前言 一、建立项目及新建Java类 二、输入简单代码实现输出 前言 1.本文所讲的是java程序设计语言&#xff0c;其内容是如何在id中新建一个项目&#xff0c;并新建一个Java文件&#xff0c;在其内输入相关代码并对其输出&#xff1b; 2.Java文件的编写以收入到我的专栏…

JDBC实现对单表数据增、删、改、查

文章目录 API介绍获取 Statement 对象Statement的API介绍使用步骤案例代码 JDBC实现对单表数据查询ResultSet的原理ResultSet获取数据的API使用JDBC查询数据库中的数据的步骤案例代码 API介绍 获取 Statement 对象 在java.sql.Connection接口中有如下方法获取到Statement对象…

IM系统完结了,那简历该怎么写?(含简历项目描述)

作者&#xff1a;冰河 星球&#xff1a;http://m6z.cn/6aeFbs 博客&#xff1a;https://binghe.gitcode.host 文章汇总&#xff1a;https://binghe.gitcode.host/md/all/all.html 星球项目地址&#xff1a;https://binghe.gitcode.host/md/zsxq/introduce.html 沉淀&#xff0c…

开放式耳机排行榜前十名?分享四款高性价比的开放式蓝牙耳机

开放式耳机并不一定要选价格贵的才好&#xff0c;而是应该按照个人需求来选择合适的开放式耳机产品&#xff0c;适合自己的才是最好。而且开放式耳机的价格区间也很广&#xff0c;从几十元到上千元不等&#xff0c;在每个价位区间里都有属于每个价位区间的高性价比耳机。选择耳…

RusTitW:大规模语言视觉文本识别数据集(猫脸码客 第190期)

RusTitW: Russian Language Visual Text Recognition 一、引言 在信息爆炸的现代社会&#xff0c;文本作为信息传递的重要载体&#xff0c;扮演着不可或缺的角色。随着计算机视觉与模式识别技术的飞速发展&#xff0c;自动化文本识别&#xff08;OCR, Optical Character Reco…

【LabVIEW学习篇 - 25】:JKI状态机

文章目录 JKI状态机JKI状态机安装JKI状态机的基本了解状态机的运行原理示例 JKI状态机 JKI状态机的核心就是队列消息状态机用户事件处理器模式&#xff0c;JKI状态机采用指定格式的字符串来描述状态。 JKI状态机并没有采用队列而是采用指定的字符串进行存储&#xff0c;它封装…

用EA和SysML一步步建模(07)蒸馏器系统上下文图01

用EA和SysML一步步建模的操作指南&#xff08;01&#xff09; 用EA和SysML一步步建模&#xff08;02&#xff09;导入ISO-80000 用EA和SysML一步步建模&#xff08;03&#xff09;创建包图和包的关系 用EA和SysML一步步建模&#xff08;04&#xff09;创建“需求组织”包图 …

【ACM出版】第三届人工智能与智能信息处理国际学术会议(AIIIP 2024,10月25-27)

第三届人工智能与智能信息处理国际学术会议&#xff08;AIIIP 2024&#xff09; 2024 3rd International Conference on Artificial Intelligence and Intelligent Information Processing 中国-天津 | 2024年10月25-27日 | 会议官网&#xff1a;www.aiiip.net 官方信息 会议…