【数据结构】AVL树(万字超详细 附动图)

一、前言

二、AVL树的性质

三、AVL树节点的定义

四、AVL树的插入

五、AVL树的平衡调整

六、AVL树的验证

6.1 验证有序

6.2 验证平衡

七、AVL树的删除

八、AVL树的性能和代码


一、前言

还没有学习过二叉搜索树的同学可以移步

【数据结构】二叉搜索树-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/Eristic0618/article/details/137919573?spm=1001.2014.3001.5501AVL树,又称为平衡二叉树,它基于二叉搜索树并通过平衡而得到。

在前面的学习中我们提到,二叉搜索树可以提高搜索数据的效率,但在数据有序的情况下会退化为单支树,此时在树中查找元素就得遍历一整个分支,时间复杂度也会退化至O(N)。

如果有一种算法,可以使二叉搜索树时刻保持左右子树的平衡,就可以避免这种最坏情况。

因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了AVL树,解决了上述问题。


二、AVL树的性质

当我们向二叉搜索树中插入新节点时,如果能用某种方法时刻保证树中每个节点的左右子树高度之差不超过1,就可以降低整棵树的高度,保证每条分支的平衡

AVL树的性质如下:

  • AVL树可以是空树
  • 一颗AVL树的左右子树都是AVL树
  • 一颗AVL树的左右子树高度差不超过1

例如:


三、AVL树节点的定义

AVL树的左右子树高度差不能超过1,但是如何便捷的去检测该性质是否被打破呢?

我们可以在节点中定义一个平衡因子,如果左子树比右子树高一层,那么平衡因子就为-1;如果左右子树一样高,平衡因子就为0;如果右子树比左子树高一层,那么平衡因子就为1,这三种情况下AVL树的性质都没有被打破。

按照这个规则,如果平衡因子为-2、2或其他值,则说明左右子树已经失衡,性质被打破。

在调整失衡的AVL树时,我们需要频繁的访问父节点,所以在AVL树中我们需要使用三叉链,因此AVL树的节点除了包含左右子节点的指针,还需要一个指向父节点的指针

另外需要说明一下,本文中,我们使用key/value模型的AVL树

AVL树节点的定义如下:

template<class K,class V>
struct AVLTreeNode
{AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;pair<K, V> _kv; //第一个数据存储key,第二个数据存储valueint _bf; //平衡因子(balance factor)AVLTreeNode(const pair<const K, V>& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_bf(0) //新节点左右都为空,平衡因子为0{}
};

可能有些同学对pair没有了解,这里简单介绍一下

pair可以将两个数据组成一组元素,因此对于key/value模型这种需要用到两个数据为一组的元素时就可以使用,内部的成员变量为first和second,其主要使用方法为:

pair<T1, T2> p1(v1, v2); //输入两个数据创建pair类型变量
make_pair(v1, v2);       //输入两个数据通过函数创建pair类型变量
p1.first                 //访问p1的第一个数据
p1.second                //访问p1的第二个数据


四、AVL树的插入

向AVL树中插入节点与向二叉搜索树中插入节点的过程基本相同,唯一的区别就是AVL树在插入节点后可能存在失衡的情况,需要调整。

我们先按照二叉搜索树的规则将节点插入到AVL树中,并判断插入的节点在父节点的左边还是右边

按照平衡因子的规则,如果新节点插入到了父节点的左侧,那么父节点的平衡因子-1

如果新节点插入到了父节点的右侧,那么父节点的平衡因子+1

以上,便是新增节点的父节点平衡因子可能的变化情况。

但是!插入一个节点不但会影响父节点,还可能会影响到祖先节点

我们观察上面的四种可能,其中左边的两种情况下,插入节点后以父节点为根的子树高度发生了变化;在右边的两种情况下,插入节点后以父节点为根的子树高度没有发生变化。

观察过后可以发现,当父节点的平衡因子从0变为1/-1后,子树高度发生变化;当父节点的平衡因子从1/-1变为0后,子树高度不发生变化 

如果以父节点为根的子树高度没有发生变化,那么就不会影响到祖先节点的平衡因子;如果高度变了就会继续向上影响到祖先节点的平衡因子

因此,我们可以通过判断节点的插入位置计算父节点的平衡因子,进而判断子树高度是否发生变化,再进一步计算对祖先节点平衡因子的影响,来判断AVL树是否失衡

至此,我们已经可以开始写插入新节点和更新平衡因子的代码了:

template<class K, class V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;
public:bool insert(const pair<const K, V>& kv){if (_root == nullptr) //检测为空树的情况{_root = new Node(kv);return true;}Node* parent = nullptr;Node* cur = _root;while (cur) //搜索新节点的插入位置{parent = cur;if (kv.first > cur->_kv.first)cur = cur->_right;else if (kv.first < cur->_kv.first)cur = cur->_left;elsereturn false;}cur = new Node(kv);//将父节点与新节点链接//比较新节点和父节点的key判断插入到左边还是右边if (kv.first > parent->_kv.first) //这里防止有人看不懂再强调一遍,kv是pair类型的对象,kv.first是key,kv.second是value{parent->_right = cur;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}while (cur != _root){//插入节点后除了对父节点造成影响还可能对祖宗节点造成影响//因此随着循环进行,这里的cur不一定为新节点,可以理解为高度发生变化的子树的根节点//更新父节点的平衡因子if (cur == parent->_left)parent->_bf--;elseparent->_bf++;//更新后检测父节点的平衡因子if (parent->_bf == 0) //平衡因子为0说明没有打破性质,跳出循环break;else if (parent->_bf == 1 || parent->_bf == -1) //更新后平衡因子为1或-1说明高度发生变化,改变cur和parent的指向后继续向上更新{cur = parent;parent = parent->_parent;}else if (parent->_bf == 2 || parent->_bf == -2) //更新后平衡因子为2或-2.说明已经失衡,需要调整{//不同情况的调整方法...if (parent->_bf == 2){if (cur->_bf == 1){//...}else if (cur->_bf == -1){//...}}else{if (cur->_bf == 1){//...}else if (cur->_bf == -1){//...}}break;}else //平衡因子出现意外情况,报错{assert(false);}}return true;}private:Node* _root = nullptr;
};


五、AVL树的平衡调整(附动图)

如果在一颗原本平衡的AVL树中插入一个新节点,可能会造成失衡,此时需要调整树的结构使之重新平衡,这种调整方法称为旋转

根据树的原本结构和节点插入位置的不同分为四种情况四种旋转方式

(1)新节点插入较高左子树左侧:右单旋

自己做的动图大家有需要自取~这里就不加水印了

问题来了:如何判断插入的新节点的方位呢?

很简单,以上面的情况为例,插入新节点后60的平衡因子变成-2,说明左子树更高,而30的平衡因子变成-1,说明新节点插入到了30的左子树。后面左单旋以及双旋中都同理,我们使用平衡因子就可以判断新节点插入的位置

右单旋代码如下:

void RotateRight(Node *parent) //parent为平衡因子发生失衡的节点
{Node *subL = parent->_left; //subL为parent的左子节点Node *subLR = subL->_right; //subLR为subL的右子节点//parent,subL和subLR三个节点是旋转中唯三需要进行操作的三个节点// 将parent与subLR节点进行链接parent->_left = subLR;if (subLR) //subLR可能为空subLR->_parent = parent;Node *parentParent = parent->_parent; //记录parent的父节点if (parent != _root){subL->_parent = parentParent; //将subL与parent的父节点链接if (parent == parentParent->_left)parentParent->_left = subL;elseparentParent->_right = subL;}else //如果parent为根,旋转后subL成为新的根{_root = subL;subL->_parent = nullptr;}//将subL与parent链接subL->_right = parent;parent->_parent = subL;parent->_bf = subL->_bf = 0; //更新平衡因子
}

(2)新节点插入较高右子树右侧:左单旋

因为左单旋的原理和右单旋是类似的,只要理解了右单旋,加上动图的配合,左单旋和后面的双旋都是很好理解的 

左单旋代码如下:

void RotateLeft(Node *parent)
{Node *subR = parent->_right;Node *subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node *parentParent = parent->_parent;if (parent != _root){subR->_parent = parentParent;if (parent == parentParent->_left)parentParent->_left = subR;elseparentParent->_right = subR;}else{_root = subR;subR->_parent = nullptr;}subR->_left = parent;parent->_parent = subR;parent->_bf = subR->_bf = 0;
}

(3)新节点插入较高左子树右侧:先左单旋再右单旋(左右双旋) 

这种情况又可以分为两种情况:

不过这两种情况都属于在较高左子树的右侧插入,处理方式都是相同的,唯一的区别在于最后旋转完成后,更新平衡因子时的值不同。

接下来我们以上面的那个情况为例展示左右双旋的过程:

而下面的情况和上面的情况唯一的区别在于,最后更新的平衡因子不同

如何去决定每个节点更新后的平衡因子呢?可以看到这两种情况中,如果在b下面插入新节点,那么旋转过后30和60的平衡因子更新成0,90的平衡因子更新成1;如果在c下面插入新节点,则是60和90的平衡因子更新成0,30的平衡因子更新成-1

而新节点究竟插入到了b下面还是在c下面,我们可以通过插入节点后60的平衡因子来判断

左右双旋代码如下:

void RotateLR(Node *parent)
{Node *subL = parent->_left;Node *subLR = subL->_right;int bf = subLR->_bf; //记录插入节点后subLR的平衡因子RotateLeft(subL); //先左单旋RotateRight(parent); //再右单旋//更新平衡因子//通过前面记录的平衡因子判断更新的情况if (bf == 0){parent->_bf = subL->_bf = subLR->_bf = 0;}else if (bf == 1){subL->_bf = -1;parent->_bf = subLR->_bf = 0;}else if (bf == -1){parent->_bf = 1;subL->_bf = subLR->_bf = 0;}else{assert(false);}
}

(4)新节点插入较高右子树左侧:先右单旋再左单旋(右左双旋)

这种情况和左右双旋的情况原理一样,我们直接上动图和代码

右左双旋的代码如下:

void RotateRL(Node *parent)
{Node *subR = parent->_right;Node *subRL = subR->_left;int bf = subRL->_bf;RotateRight(subR);RotateLeft(parent);if (bf == 0){parent->_bf = subR->_bf = subRL->_bf = 0;}else if (bf == 1){parent->_bf = -1;subR->_bf = subRL->_bf = 0;}else if (bf == -1){subR->_bf = 1;parent->_bf = subRL->_bf = 0;}else{assert(false);}
}

现在四个旋转的函数都实现了,完整的插入函数代码如下:

bool insert(const pair<const K, V> &kv)
{if (_root == nullptr){_root = new Node(kv);return true;}Node *parent = nullptr;Node *cur = _root;while (cur){parent = cur;if (kv.first > cur->_kv.first)cur = cur->_right;else if (kv.first < cur->_kv.first)cur = cur->_left;elsereturn false;}cur = new Node(kv);if (kv.first > parent->_kv.first){parent->_right = cur;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}while (cur != _root){if (cur == parent->_left)parent->_bf--;elseparent->_bf++;if (parent->_bf == 0)break;else if (parent->_bf == 1 || parent->_bf == -1){cur = parent;parent = parent->_parent;}else if (parent->_bf == 2 || parent->_bf == -2){if (parent->_bf == 2) //说明右子树更高{if (cur->_bf == 1) //说明新节点插入到了右边,符合在更高右子树的右侧插入的情况{RotateLeft(parent); //执行左单旋}else if (cur->_bf == -1) //说明新节点插入到了左边,符合在更高右子树的左侧插入的情况{RotateRL(parent); //执行右左双旋}}else //左子树更高{if (cur->_bf == 1) //说明新节点插入到了右边,符合在更高左子树的右侧插入的情况{RotateLR(parent); //执行左右双旋}else if (cur->_bf == -1) //说明新节点插入到了左边,符合在更高左子树的左侧插入的情况{RotateRight(parent); //执行右单旋}}break;}else{assert(false);}}return true;
}


六、AVL树的验证

6.1 验证有序

最重要的插入节点部分完成了,不过在验证是否符合AVL树性质前,我们首先需要验证其是否是一棵二叉搜索树

在之前讲解二叉搜索树中提到过,如果中序遍历能够得到一个有序的序列,就说明是二叉搜索树

中序遍历代码如下:

void InOrder()
{_InOrder(_root);cout << endl;
}void _InOrder(Node *root)
{if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << " "; // key/value模型,我们只打印key即可_InOrder(root->_right);
}

验证:

说明符合二叉搜索树性质

6.2 验证平衡

要验证是否符合AVL树性质,只需要检测它的所有节点的子树高度差不超过1即可

需要注意的是,这里不可以直接通过判断平衡因子的绝对值是否大于1来验证平衡,因为平衡因子是不客观的,可以被修改

因此,我们通过递归来得到每棵子树的高度并进行判断即可

代码如下:

bool IsBalance()
{return _IsBalance(_root);
}bool _IsBalance(Node *root)
{if (root == nullptr)return true;int leftHeigit = _Height(root->_left);int rightHeight = _Height(root->_right);if (rightHeight - leftHeigit != root->_bf){cout << root->_kv.first << "平衡因子异常" << endl;return false;}return abs(rightHeight - leftHeigit) <= 1 && _IsBalance(root->_left) && _IsBalance(root->_right);
}int Height()
{return _Height(_root);
}int _Height(Node *root)
{if (root == nullptr)return 0;int higher = max(_Height(root->_left), _Height(root->_right));return higher + 1;
}

验证:

如果在验证是否是AVL树中需要更多大量的测试用例,我们可以取一些随机数:

int main()
{const int N = 1000;vector<int> v;v.reserve(N);srand(time(0));for (int i = 0; i < N; i++){v.push_back(rand() + i);}AVLTree<int, int> t;for (auto i : v){t.insert(make_pair(i, 1));}t.InOrder();if (t.IsBalance())cout << "是AVL树" << endl;elsecout << "不是AVL树" << endl;return 0;
}

 


七、AVL树的删除

AVL树的删除并不是本文的重点,因为其原理我们在前面已经学习过了

我们只需要按照二叉搜索树的方式来对目标节点进行删除,再判断是否失衡,如果失衡则旋转即可


八、AVL树的性能和代码

AVL树追求的是严格平衡,因此可以保证查找时高效的时间复杂度O(logN),但是如果我们需要频繁的对其进行旋转来维护平衡,一定程度上会影响效率,尤其是删除节点时的最差情况下我们可能需要一路旋转到根的位置。

相对于AVL树的严格平衡,红黑树则追求一种相对平衡,因此会略胜一筹,后面的文章中会对红黑树进行讲解。

AVL树的完整代码如下:

template<class K,class V>
struct AVLTreeNode
{AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;pair<K, V> _kv;int _bf; //平衡因子AVLTreeNode(const pair<const K, V>& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_bf(0){}
};template<class K, class V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;
public:bool insert(const pair<const K, V>& kv){if (_root == nullptr){_root = new Node(kv);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){parent = cur;if (kv.first > cur->_kv.first)cur = cur->_right;else if (kv.first < cur->_kv.first)cur = cur->_left;elsereturn false;}cur = new Node(kv);if (kv.first > parent->_kv.first){parent->_right = cur;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}while (cur != _root){if (cur == parent->_left)parent->_bf--;elseparent->_bf++;if (parent->_bf == 0)break;else if (parent->_bf == 1 || parent->_bf == -1){cur = parent;parent = parent->_parent;}else if (parent->_bf == 2 || parent->_bf == -2)//平衡异常{if (parent->_bf == 2){if (cur->_bf == 1){RotateLeft(parent);}else if (cur->_bf == -1){RotateRL(parent);}}else{if (cur->_bf == 1){RotateLR(parent);}else if (cur->_bf == -1){RotateRight(parent);}}break;}else{assert(false);}}return true;}void RotateLeft(Node* parent) //新节点插入较高右子树的右侧:左单旋{Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if(subRL)subRL->_parent = parent;Node* parentParent = parent->_parent;if (parent != _root){subR->_parent = parentParent;if (parent == parentParent->_left)parentParent->_left = subR;elseparentParent->_right = subR;}else{_root = subR;subR->_parent = nullptr;}subR->_left = parent;parent->_parent = subR;parent->_bf = subR->_bf = 0;}void RotateRight(Node* parent) //新节点插入较高左子树的左侧:右单旋{Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* parentParent = parent->_parent;if (parent != _root){subL->_parent = parentParent;if (parent == parentParent->_left)parentParent->_left = subL;elseparentParent->_right = subL;}else{_root = subL;subL->_parent = nullptr;}subL->_right = parent;parent->_parent = subL;parent->_bf = subL->_bf = 0;}void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateRight(subR);RotateLeft(parent);if (bf == 0){parent->_bf = subR->_bf = subRL->_bf = 0;}else if (bf == 1){parent->_bf = -1;subR->_bf = subRL->_bf = 0;}else if (bf == -1){subR->_bf = 1;parent->_bf = subRL->_bf = 0;}else{assert(false);}}void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateLeft(subL);RotateRight(parent);if (bf == 0){parent->_bf = subL->_bf = subLR->_bf = 0;}else if (bf == 1){subL->_bf = -1;parent->_bf = subLR->_bf = 0;}else if (bf == -1){parent->_bf = 1;subL->_bf = subLR->_bf = 0;}else{assert(false);}}void InOrder(){_InOrder(_root);cout << endl;}bool IsBalance(){return _IsBalance(_root);}int Height(){return _Height(_root);}size_t Size(){return _Size(_root);}Node* Find(const K& key){Node* cur = _root;while (cur){if (key > cur->_kv.first)cur = cur->_right;else if (key < cur->_kv.first)cur = cur->_left;elsereturn cur;}return nullptr;}
private:void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << " ";_InOrder(root->_right);}bool _IsBalance(Node* root){if (root == nullptr)return true;int leftHeigit = _Height(root->_left);int rightHeight = _Height(root->_right);if (rightHeight - leftHeigit != root->_bf){cout << root->_kv.first << "平衡因子异常" << endl;return false;}return abs(rightHeight - leftHeigit) <= 1 && _IsBalance(root->_left)&& _IsBalance(root->_right);}int _Height(Node* root){if (root == nullptr)return 0;int higher = max(_Height(root->_left), _Height(root->_right));return higher + 1;}size_t _Size(Node* root){if (root == nullptr)return 0;return _Size(root->_left) + _Size(root->_right) + 1;}
private:Node* _root = nullptr;
};

如果有错误的地方,欢迎在评论区指出

完.

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

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

相关文章

【C++】:构造函数和析构函数

目录 前言一&#xff0c;构造函数**1.1 什么是构造函数****1.2 构造函数的特性**1.3 总结 二&#xff0c;析构函数**2.1 什么是析构函数****2.2 析构函数的特性****2.3 总结** 前言 如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中真的什么都没有吗&#xff1f;并…

WebGL绘制和变换三角形

1、绘制多个点 构建三维模型的基本单位是三角形。不管三维模型的形状多么复杂&#xff0c;其基本组成部分都是三角形&#xff0c;只不过复杂的模型由更多的三角形构成而已。 gl.vertexAttrib3f()一次只能向顶点着色器传入一个顶点&#xff0c;而绘制三角形、矩形和立方体等&am…

mysql内存优化

临时更改 查看缓冲池大小 show variables like innodb_buffer_pool_size%;通过sql修改&#xff0c;重启后会失效 SET GLOBAL innodb_buffer_pool_size 4294967296;字节转换网站 永久更改 通过my.cnf配置修改&#xff0c;永久生效 进入mysql容器&#xff0c;vi /etc/my.cnf…

良品生活(C端产品设计)

一、产品定义 用户 人群&#xff1a;有某种兴趣的人群&#xff08;喜欢无印良品的商品&#xff09;。 需求&#xff1a;对某种兴趣有内容消费需求&#xff0c;同时也有与别人分享内容的兴趣。 角色&#xff1a;普通用户&#xff0c;内容创作者。 公司 诉求&#xff1a;通…

绿联 安装transmission

绿联 安装transmission及中文UI 1、镜像 linuxserver/transmission:latest 2、安装 2.1、创建容器 按需配置权重。 2.2、基础设置 2.3、网络 桥接即可。 注&#xff1a;如果使用IPV6&#xff0c;请选择"host"模式。 注&#xff1a;如果使用IPV6&#xff0c;请选…

武汉大学博士,华为上班5年多,月薪多少。。。

最近&#xff0c;一位来自武汉大学的博士研究生透露了自己在华为公司工作五年后的薪酬情况。 据他透露&#xff0c;他在2018年加入华为时的月薪为2.4万&#xff0c;随着时间的推移&#xff0c;到了2023年&#xff0c;他的月薪已经增长至4.4万&#xff01;此外&#xff0c;他还透…

某翻译平台翻译接口逆向之加解密参数刨析

上文链接 某翻译平台翻译接口逆向之webpack学习 分析参数 加密参数&#xff1a; ${t} function S(e, t) {return _(client${u}&mysticTime${e}&product${d}&key${t}) } function k(e, t) {const n (new Date).getTime();return {sign: S(n, e),client: u,produc…

vue实现水平排列且水平居中

样式实现 .body{text-align: center; } .body_content{display: inline-block; } .body_content_cardList{display: flex;flex-wrap: wrap;text-align: center; }<div class"body"><div class"body_content"><div class"body_content…

一模块多功能:钡铼IOy系列模块将开关输出输入与模拟量测量相结合

钡铼IOy系列模块是一款具有多功能特性的智能设备&#xff0c;它将开关输出输入与模拟量测量相结合&#xff0c;为工业自动化领域带来了全新的解决方案。这一系列模块的设计理念在于提供更为灵活和全面的监控与控制功能&#xff0c;以满足工业生产中的多样化需求&#xff0c;从而…

SpringBoot 根据不同环境切换不同文件路径

最简单的办法就是使用多个 application.yml 配置文件 。一个叫 application-test.yml 测试用&#xff1b;另一个是正式使用的 application-prod.yml 。win环境下大部分是开发测试时候使用的&#xff0c;服务正式上线需要部署在Linux服务器上又换成了Linux。但开发初期或者项目…

CANN 开发工具介绍

1、ATC工具 ATC&#xff08;Ascend Tensor Compiler&#xff09;是异构计 算架构CANN体系下的模型转换工具&#xff0c; 它可 以将开源框架的网络模型以及Ascend IR定义 的单算子描述文件&#xff08;json格式&#xff09;转换为昇腾 AI处理器支持的.om格式离线模型。 2、精度…

jdbc操作数据库 and 一个商品管理页面

文章目录 1. 介绍1.1 应用知识介绍1.2 项目介绍 2. 文件目录2.1 目录2.2 介绍以下&#xff08;从上到下&#xff09; 3. 相关代码3.1 DBConnection.java3.2 MysqlUtil.java3.3 AddServlet.java3.4 CommodityServlet.java3.5 DelectServlet.java3.6 SelectByIdServlet.java3.7 S…

解线性方程组——(Jacobi)雅克比迭代法 | 北太天元

一、Jacobi迭代法 n 3 n3 n3 , 阶数为 3 时 A ( a 11 a 12 a 13 a 21 a 22 a 23 a 31 a 32 a 33 ) , b ( b 1 b 2 b 3 ) , A\begin{pmatrix} a_{11} & a_{12} &a_{13}\\ a_{21} & a_{22} &a_{23}\\ a_{31} & a_{32} &a_{33}\\ \end{pmatrix} ,\qua…

广东海洋大学成功部署(泰迪智能科技)大数据人工智能实验室建设

广东海洋大学简称广东海大&#xff0c;坐落于广东省湛江市&#xff0c;是国家海洋局与广东省人民政府共建的省属重点建设大学、广东省高水平大学重点学科建设高校、粤港澳高校联盟成员 &#xff0c;入选卓越农林人才教育培养计划&#xff0c;是教育部本科教学水平评估优秀院校。…

vuex中mutations和actions 异步同步实现方法

一 mutations 和 actions 优缺点及使用场景 同步性&#xff1a; Mutations是同步的&#xff0c;这意味着它们会在提交后立即执行。而Actions是异步的&#xff0c;提交后会被排队&#xff0c;在稍后执行。 用途&#xff1a; Mutations适用于简单的状态修改&#xff0c;如递增/…

【Java基础】压测工具JMeter使用简介

1. JMeter介绍 Apache JMeter是一个基于Java开发的开源性能测试工具&#xff0c;由Apache软件基金会维护 JMeter最初设计用于Web应用测试&#xff0c;但它的功能已经扩展到其他测试领域。JMeter可以用于测试静态和动态资源&#xff0c;如静态文件、Java小服务程序、CGI脚本、J…

力扣HOT100 - 543. 二叉树的直径

解题思路&#xff1a; class Solution {int ans;//记录节点数public int diameterOfBinaryTree(TreeNode root) {ans 1;depth(root);return ans - 1;//节点数减 1 就是路径长度}public int depth(TreeNode root) {if (root null) return 0;int l depth(root.left);int r de…

中红医疗:纷享销客CRM系统如何助力​数字化“狂飙”

纷享销客深耕 CRM 多年&#xff0c;可以顺畅打通 CRM 和 ERP 系统客户资源池&#xff0c;将金蝶苍穹平台的物料、产品基础主数据作为档案同步到纷享销客&#xff0c;以便商务维护好产品及库存。 纷享销客通过成熟的集成方案提高系统耦合性&#xff0c;让销售实时获得新产品及营…

123.Mit6.S081-实验1-Xv6 and Unix utilities

今天我们来进行Mit6.S081实验一的内容。 实验任务 一、启动xv6(难度&#xff1a;Easy) 获取实验室的xv6源代码并切换到util分支。 $ git clone git://g.csail.mit.edu/xv6-labs-2020 Cloning into xv6-labs-2020... ... $ cd xv6-labs-2020 $ git checkout util Branch util …