【数据结构】AVL树(图文解析 + 代码实现)

目录

1、AVL树的概念

2、AVL树结点的定义

3、AVL树的插入

4、AVL树的旋转

4.1 左单旋

4.2 右单旋

4.3 右左双旋

4.4 左右双旋

5、AVL树的验证

6、AVL树的性能

前面对map/multimap/set/multiset进行了简单的介绍,会大仙,这几个容器有个共同点是:其底层都是按照二叉搜索树来实现的,但是二叉搜索树有其自身的缺陷,假如往树中插入的元素有序或者接近有序,二叉搜索树就会退化成单支树,时间复杂度会退化成O(N),因此map、set等关联式容器的底层结构是对二叉树进行了平衡处理,即采用平衡树来实现。本章介绍的AVL树和下一章介绍的红黑树,就是平衡树

1、AVL树的概念

当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

为什么不将树调整为左右子树的高度相等呢?因为有些结点数量下(如2和4个结点),做不到左右子树高度相等,所以最好的平衡二叉树就是高度不超过1

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

上面这棵树的平衡因子是用右子树高度-左子树高度,这不是一定的,也可以反过来,并没有严格规定,这篇文章中的平衡因子都是用右子树高度-左子树高度的

因为AVL树每个结点的左右子树高度差不超过1,所以平衡因子只有在-1、0、1是才是正常的

2、AVL树结点的定义

我们这里实现的是KV模型的AVL树,为了与map对应,两个值是以pair的形式存储。并且每个结点中还要增加一个平衡因子。当插入新结点时,为了检查路径上是否出现异常的平衡因子,还要增加一个指向父亲结点的指针,另外原来还有指向左右孩子的指针,称为三叉链结构

template<class K,class V>
struct AVLTreeNode
{pair<K, V> _kv;// 三叉链AVLTreeNode* _left;AVLTreeNode* _right;AVLTreeNode* _parent;int _bf; // 平衡因子AVLTreeNode(const pair<K,V>& kv):_kv(kv),_left(nullptr),_right(nullptr),_parent(nullptr),_bf(0){}
};

根结点的_parent置为nullptr 

3、AVL树的插入

向AVL树中插入一个新结点时,插入的操作是和二叉搜索树一致的,只是插入一个结点后,需要更新一下这个结点到这棵树的根节点路径上部分结点的平衡因子,若是平衡因子出现异常,则需要进行旋转操作。

插入结点,会影响部分祖先结点的平衡因子。因为这里的平衡因子 = 右子树高度 - 左子树高度

结点插入在左子树,其父亲的平衡因子--

结点插入在右子树,其父亲的平衡因子++

是否需要继续往上更新祖先,要看parent所在子树的高度是否发生了变化,此时有3种情况:

1. 更新后parent的平衡因子变成0

说明没插入时parent的平衡因子是-1/1,一边高一边低,插入后,变成两边一样高,parent所在子树的高度没有发生变化,所以不需要向上更新

2. 更新后parent的平衡因子变成-1/1

说明没插入时parent的平衡因子是0,两边一样高,插入后,变成一边高一边低,parent所在子树的高度变高了,所以需要继续向上更新

3. 更新后parent的平衡因子变成-2/2

说明没插入时parent的平衡因子是-1/1,插入结点插入在高的那一边,进一步加剧了parent所在子树的不平衡,已经违反规定,需要旋转处理

bool Insert(const pair<K, V>& kv)
{if (_root == nullptr){_root = new Node(kv);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;// 更新平衡因子while (parent){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){// 不平衡了,旋转处理}else{assert(false);}}return true;
}

4、AVL树的旋转

4.1 左单旋

当新结点插入到右子树的右侧,需要进行左单旋 ----- 右右:左单旋

这里面h>=0,表示a、b、c是高度为h的AVL子树

左单旋的标志就是出现异常的结点的平衡因子为2,并且其右子树的平衡因子为1,那么这个时候就进行左单旋。

左单旋步骤:

1. 将subRL变成parent的右子树

2. 将parent变成subR的左子树

3. 建立subR与parent的父亲结点parentParent的关系

4. 更新parent和subR的平衡因子 

完成代码时,除了要完成每个结点的链接关系,还需要注意重新链接后结点父亲的变化和平衡因子的变化。其中平衡因子只有30和60这两个结点有变化,因为要改变平衡因子,需要改变左右子树的高度,并且这两个结点的平衡因子都变成了0。注意,subRL是有可能为nullptr的,当h等于0时,所以修改其父亲结点时需要判断一下。

void RotateL(Node* parent) // 左单旋
{Node* subR = parent->_right;Node* subRL = subR->_left;Node* parentParent = parent->_parent;// 1. 将subRL变成parent的右子树parent->_right = subRL;if (subRL)subRL->_parent = parent;// 2. 将parent变成subR的左子树subR->_left = parent;parent->_parent = subR;// 3. 建立subR与parent的父亲结点parentParent的关系if (parentParent == nullptr) // 说明parent就是根结点{_root = subR;subR->_parent = nullptr;}else{if (parent == parentParent->_left)parentParent->_left = subR;elseparentParent->_right = subR;subR->_parent = parentParent;}// 4. 更新parent和subR的平衡因子 parent->_bf = subR->_bf = 0;
}

4.2 右单旋

当新插入的结点在左子树的左侧,需要进行右单旋 ----- 左左:右单旋

右单旋的标志就是出现异常的结点的平衡因子为-2,并且其左子树的平衡因子为-1,那么这个时候就进行左单旋。 

右单旋的步骤:

1. 将subLR变成parent的左子树

2. 将parent变成subL的右子树

3. 建立subL与parent的父亲结点parentParent的关系

4. 更新parent和subL的平衡因子 

void RotateR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;Node* parentParent = parent->_parent;// 1. 将subLR变成parent的左子树parent->_left = subLR;if (subLR)subLR->_parent = parent;// 2. 将parent变成subL的右子树subL->_right = parent;parent->_parent = subL;// 3. 建立subL与parent的父亲结点parentParent的关系if (parentParent == nullptr) // 说明parent就是根结点{_root = subL;subL->_parent = nullptr;}else{if (parentParent->_left == parent)parentParent->_left = subL;elseparentParent->_right = subL;subL->_parent = parentParent;}// 4. 更新parent和subL的平衡因子 parent->_bf = subL->_bf = 0;
}

4.3 右左双旋

当新插入的结点在右子树的左侧,需要先进行右单旋,再进行左单旋 ----- 右左:右左双旋

右左双旋的标志是出现异常的结点的平衡因子是2,其右子树的平衡因子是-1

第一种情况:在右子树的左子树的右子树插入(即c子树插入)

  第二种情况:在右子树的左子树的左子树插入(即b子树插入)

这两种情况都是先进行右单旋,然后进行左单旋,只是旋转完成后,个别结点的平衡因子不同。如何区分是在b插入还是c插入呢?

当h > 0时

1. 插入结点后,若右子树的左子树这个结点的平衡因子是1,则是在c插入的

2. 插入结点后,若右子树的左子树这个结点的平衡因子是-1,则是在b插入的

上面两幅图就是h > 0的两种情况的图

当h == 0 时

3. 插入之前连60这个结点都没有,60这个结点自己就是新增,此时60这个结点的平衡因子是0

右左单旋步骤:

1、计算subRL的平衡因子

2、对subR进行右单旋

3、对parent进行左单旋

4、更新parent、subR、subRL的平衡因子

void RotateRL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;// 1、计算subRL的平衡因子int bf = subRL->_bf;// 2、对subR进行右单旋RotateR(subR);// 3、对parent进行左单旋RotateL(parent);// 4、更新parent、subR、subRL的平衡因子if (bf == 0){subR->_bf = 0;subRL->_bf = 0;parent->_bf = 0;}else if (bf == 1){subR->_bf = 0;subRL->_bf = 0;parent->_bf = -1;}else if (bf == -1){subR->_bf = 1;subRL->_bf = 0;parent->_bf = 0;}else{assert(false);}
}

4.4 左右双旋

当新插入的结点在左子树的右侧,需要先进行左单旋,再进行右单旋 ----- 左右:左右双旋

左右双旋的标志是出现异常的结点的平衡因子是-2,其左子树的平衡因子是1

第一种情况:在左子树的右子树的左子树插入(即b子树插入)

第二种情况:在左子树的右子树的右子树插入(即c子树插入)

第三种情况:当h == 0时

左右单旋的步骤:

1. 计算subLR的平衡因子

2. 对subL进行左单旋

3. 对parent进行右单旋

4. 更新parent、subR、subRL的平衡因子

void RotateLR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;// 1. 计算subLR的平衡因子int bf = subLR->_bf;// 2. 对subL进行左单旋RotateL(subL);// 3. 对parent进行右单旋RotateR(parent);// 4. 更新parent、subR、subRL的平衡因子if (bf == 0){subL->_bf = 0;subLR->_bf = 0;parent->_bf = 0;}else if (bf == 1){subL->_bf = -1;subLR->_bf = 0;parent->_bf = 0;}else if (bf == -1){subL->_bf = 0;subLR->_bf = 0;parent->_bf = 0;}else{assert(false);}
}

会发现同号单旋,异号双旋

所以此时插入操作的代码为

bool Insert(const pair<K, V>& kv)
{// 若_root为空,直接让新插入的结点变成根if (_root == nullptr){_root = new Node(kv);return true;}// 若_root不为空,按二叉搜索树的规则找到插入的位置,同时也要记录父亲结点Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}// 找到要插入的位置后,根据传过来的值创建一个结点,然后判断其是父亲节点的左边还是右边,链接上去cur = new Node(kv);if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}// 因为现在是三叉链结果,还要链接一下父亲结点cur->_parent = parent;// 更新平衡因子while (parent){// 更新当前结点的父亲结点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 && parent->_right->_bf == 1){RotateL(parent);}else if (parent->_bf == -2 && parent->_left->_bf == -1){RotateR(parent);}else if (parent->_bf == 2 && parent->_right->_bf == -1){RotateRL(parent);}else{RotateLR(parent);}break; // 旋转完后,一定平衡了,所以可以跳出循环}else{assert(false);}}return true;
}

注意,旋转完成之后,这颗子树的父亲结点的平衡因子一定是0,所以可以直接跳出循环,不需要继续向上更新平衡因子

5、AVL树的验证

在前面,我们已经实现了插入操作,那么按照这个插入函数获得的真的是一颗AVL树吗?

所以,我们需要进行验证。验证只需要计算每个结点的左右树高,判断左右树高的差值小于2即可

int _Height(Node* root)
{if (root == nullptr)	return 0;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
bool _IsBalanceTree(Node* root)
{// 空树也是AVL树if (root == nullptr) return true;// 计算pRoot节点的平衡因子:即pRoot左右子树的高度差int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);int diff = rightHeight - leftHeight;// 如果计算出的平衡因子与pRoot的平衡因子不相等,或者// pRoot平衡因子的绝对值超过1,则一定不是AVL树if (abs(diff) >= 2 || root->_bf != diff)return false;// pRoot的左和右如果都是AVL树,则该树一定是AVL树return _IsBalanceTree(root->_left) && _IsBalanceTree(root -> _right);
}

这两个函数需要写在AVL树内部,因为用到了_root

6、AVL树的性能

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即log2 (N)。但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。

template<class K,class V>
struct AVLTreeNode
{pair<K, V> _kv;// 三叉链AVLTreeNode* _left;AVLTreeNode* _right;AVLTreeNode* _parent;int _bf; // 平衡因子AVLTreeNode(const pair<K,V>& kv):_kv(kv),_left(nullptr),_right(nullptr),_parent(nullptr),_bf(0){}
};
template<class K,class V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;
public:AVLTree() = default;~AVLTree(){Destory(_root);_root = nullptr;}AVLTree(const AVLTree<K,V>& kv){_root = Copy(kv._root);}AVLTree operator=(AVLTree<K, V> kv){swap(_root, kv._root);}bool Insert(const pair<K, V>& kv){// 若_root为空,直接让新插入的结点变成根if (_root == nullptr){_root = new Node(kv);return true;}// 若_root不为空,按二叉搜索树的规则找到插入的位置,同时也要记录父亲结点Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}// 找到要插入的位置后,根据传过来的值创建一个结点,然后判断其是父亲节点的左边还是右边,链接上去cur = new Node(kv);if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}// 因为现在是三叉链结果,还要链接一下父亲结点cur->_parent = parent;// 更新平衡因子while (parent){// 更新当前结点的父亲结点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 && parent->_right->_bf == 1){RotateL(parent);}else if (parent->_bf == -2 && parent->_left->_bf == -1){RotateR(parent);}else if (parent->_bf == 2 && parent->_right->_bf == -1){RotateRL(parent);}else{RotateLR(parent);}break; // 旋转完后,一定平衡了,所以可以跳出循环}else{assert(false);}}return true;}void InOrder(){_InOrder(_root);}bool IsBalanceTree(){return _IsBalanceTree(_root);}int Height(){return _Height(_root);}int Size(){_Size(_root);}
private:int _Size(Node* root){return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;}int _Height(Node* root){if (root == nullptr)	return 0;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;}bool _IsBalanceTree(Node* root){// 空树也是AVL树if (root == nullptr) return true;// 计算pRoot节点的平衡因子:即pRoot左右子树的高度差int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);int diff = rightHeight - leftHeight;// 如果计算出的平衡因子与pRoot的平衡因子不相等,或者// pRoot平衡因子的绝对值超过1,则一定不是AVL树if (abs(diff) >= 2 || root->_bf != diff)return false;// pRoot的左和右如果都是AVL树,则该树一定是AVL树return _IsBalanceTree(root->_left) && _IsBalanceTree(root -> _right);}void RotateL(Node* parent) // 左单旋{Node* subR = parent->_right;Node* subRL = subR->_left;Node* parentParent = parent->_parent;// 1. 将subRL变成parent的右子树parent->_right = subRL;if (subRL)subRL->_parent = parent;// 2. 将parent变成subR的左子树subR->_left = parent;parent->_parent = subR;// 3. 建立subR与parent的父亲结点parentParent的关系if (parentParent == nullptr) // 说明parent就是根结点{_root = subR;subR->_parent = nullptr;}else{if (parent == parentParent->_left)parentParent->_left = subR;elseparentParent->_right = subR;subR->_parent = parentParent;}// 4. 更新parent和subR的平衡因子?parent->_bf = subR->_bf = 0;}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;Node* parentParent = parent->_parent;// 1. 将subLR变成parent的左子树parent->_left = subLR;if (subLR)subLR->_parent = parent;// 2. 将parent变成subL的右子树subL->_right = parent;parent->_parent = subL;// 3.?建立subL与parent的父亲结点parentParent的关系if (parentParent == nullptr) // 说明parent就是根结点{_root = subL;subL->_parent = nullptr;}else{if (parentParent->_left == parent)parentParent->_left = subL;elseparentParent->_right = subL;subL->_parent = parentParent;}// 4.更新parent和subL的平衡因子parent->_bf = subL->_bf = 0;}void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;// 1、计算subRL的平衡因子int bf = subRL->_bf;// 2、对subR进行右单旋RotateR(subR);// 3、对parent进行左单旋RotateL(parent);// 4、更新parent、subR、subRL的平衡因子if (bf == 0){subR->_bf = 0;subRL->_bf = 0;parent->_bf = 0;}else if (bf == 1){subR->_bf = 0;subRL->_bf = 0;parent->_bf = -1;}else if (bf == -1){subR->_bf = 1;subRL->_bf = 0;parent->_bf = 0;}else{assert(false);}}void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;// 1. 计算subLR的平衡因子int bf = subLR->_bf;// 2. 对subL进行左单旋RotateL(subL);// 3. 对parent进行右单旋RotateR(parent);// 4.?更新parent、subR、subRL的平衡因子if (bf == 0){subL->_bf = 0;subLR->_bf = 0;parent->_bf = 0;}else if (bf == 1){subL->_bf = -1;subLR->_bf = 0;parent->_bf = 0;}else if (bf == -1){subL->_bf = 0;subLR->_bf = 0;parent->_bf = 0;}else{assert(false);}}void Destory(Node* root){if (root == nullptr) return;Destory(root->_left);Destory(root->_right);delete root;}Node* Copy(Node* root){if (root == nullptr) return nullptr;Node* newNode = new Node(root->_kv);newNode->_left = Copy(root->_left);newNode->_right = Copy(root->_right);return newNode;}void _InOrder(Node* _root){if (_root == nullptr) return;_InOrder(_root->_left);cout << _root->_kv.first << "--" << _root->_kv.second << "--" << _root->_bf << endl;_InOrder(_root->_right);}Node* _root;
};

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

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

相关文章

sklearn基础学习

1. 简介 1.1 什么是sklearn sklearn&#xff0c;或者更正式地称为scikit-learn&#xff0c;是一个基于Python的开源机器学习库。它建立在NumPy、SciPy和matplotlib之上&#xff0c;提供了简单而有效的工具用于数据挖掘和数据分析。sklearn支持监督学习和无监督学习算法&#…

使用 jQuery 中的 this 实例

在 jQuery 中&#xff0c;this 关键字用于表示指向当前操作的 DOM 元素。本篇博客将详细介绍如何在 jQuery 中使用 this 实例。 一、选择器中的 this 在选择器中&#xff0c;this 可以方便地指向当前操作的 DOM 元素。例如&#xff0c;当用户点击一个按钮时&#xff0c;我们想…

【Linux常用命令】之find命令

Linux常用命令之find命令 文章目录 Linux常用命令之find命令常用命令之find背景介绍 总结 作者简介 听雨&#xff1a;一名在一线从事多年研发的程序员&#xff0c;从事网站后台开发&#xff0c;熟悉java技术栈&#xff0c;对前端技术也有研究&#xff0c;同时也是一名骑行爱好…

Linux Gui 窗口对话和窗口操作

zenity 可以实现窗口对话 eg: zenity --error --width 300 --text "Permission denied. Cannot write to the file." ChosenDate$(zenity --calendar --text "Choose a date" --title "How-To Geek Rota" --day 1 --month 9 --year 2019); …

【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !

目录 C语言指针精讲1. 什么是指针&#xff1f;1.1 指针的内存模型1.1.1 指针演示输出 1.2 指针运算1.2.1 指针算术运算输出1.2.2 指针与数组的关系输出 1.3 指针类型1.3.1 不同类型的指针示例输出1.3.2 void 指针输出 1.4 指针与内存管理动态内存分配输出 1.5 指针与内存泄漏1.…

vue 两个页面切换, 再回到当前页,还是离开前的数据

1、要保证页面的name 和 建路由的大小写一致 2、页面不用生命周期--activated 调接口刷新

在 Kubernetes 中设置 Pod 优先级及其调度策略详解

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

【论文解读】大模型算法发展

一、简要介绍 论文研究了自深度学习出现以来&#xff0c;预训练语言模型的算法的改进速度。使用Wikitext和Penn Treebank上超过200个语言模型评估的数据集(2012-2023年)&#xff0c;论文发现达到设定性能阈值所需的计算大约每8个月减半一次&#xff0c;95%置信区间约为5到14个月…

雪花算法 集群uid重复问题 uid-generator-spring-boot-starter

1、在生成环境 在某个业务使用该插件生成uid,由于业务整合了 mybatis-plus模块 2、该业务是分部署集群部署以及使用的多线程获取uid&#xff0c;使用中发现唯一建冲突&#xff0c;生成的uid有重复。 然后查看日志发现 workerId 始终为0 怀疑是生成workerId出了问题。 查看跟…

开发日志:windows修复SSL漏洞CVE-2016-2183(3389端口)

漏洞危害&#xff1a; 具有足够资源的中间人攻击者可利用此漏洞,通过“birthday”攻击检测会在固定密码与已知纯文本之间泄露 XOR 的冲突,进而泄露密码文本(例如安全 HTTPS Cookie),并可能导致劫持经认证的会话。 参见《支持SSL 64位块大小的密码套件(SWEET32)-修复方案》 参考…

数据库编程中游标 连接 commit

在数据库编程中&#xff0c;关闭游标和连接是一个重要的步骤&#xff0c;确保资源正确地释放和数据库操作的完整性。 游标&#xff08;Cursor&#xff09;和连接&#xff08;Connection&#xff09;是数据库编程中两个关键但不同的概念。它们在数据库操作中的作用和功能有所不…

100个python的基本语法知识【下】

50. 压缩文件&#xff1a; import zipfilewith zipfile.ZipFile("file.zip", "r") as zip_ref:zip_ref.extractall("extracted")51. 数据库操作&#xff1a; import sqlite3conn sqlite3.connect("my_database.db") cursor conn.c…

公告 | 长安链v2.3.4版本更新,增强功能、健全性和易用性

近期长安链长期支持版本v2.3.4发布&#xff0c;在功能、健全性和易用性方面进行了升级&#xff0c;是长安链2.3.x将近两年来的第五个版本&#xff0c;支持2.3.x版本的平滑升级&#xff0c;欢迎升级体验。 主要功能迭代 在功能上新增PK模式下支持grpcs连接、sdk支持自定义规则订…

递归神经网络(RNN)及其预测和分类的Python和MATLAB实现

递归神经网络&#xff08;Recurrent Neural Networks&#xff0c;RNN&#xff09;是一种广泛应用于序列数据建模的深度学习模型。相比于传统的前馈神经网络&#xff0c;RNN具有记忆和上下文依赖性的能力&#xff0c;适用于处理具有时序关联性的数据&#xff0c;如文本、语音、时…

主流树模型讲解、行列抽样、特征重要性梳理总结

本文旨在总结一下常见树模型的行、列抽样特点以及特征重要性的计算方式&#xff0c;也会带着过一遍算法基本原理&#xff0c;一些细节很容易忘记啊。 主要是分类和回归两类任务&#xff0c;相信能搜索这篇文章的你&#xff0c;应该对树模型有一定的了解。 可以搜索 总结 &…

java设计模式:04-03-解释器模式

解释器模式 (Interpreter Pattern) 定义 解释器模式是一种行为型设计模式&#xff0c;它提供了解释语言&#xff08;或表达式&#xff09;文法的一种方法&#xff0c;通过定义一系列语言&#xff08;或表达式&#xff09;的解释器&#xff0c;将文法中的句子转换为计算结果。…

老鼠后五毒也来凑热闹!网红食品惊现「壁虎头」,胖东来已下架…

上周&#xff0c;老鼠有点忙&#xff0c;比如其连续被曝出&#xff0c;出现在了方便面知名品牌的调料包、知名连锁餐饮品牌的黄焖鸡饭中。‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 在小柴「被「添加」进方便面、黄焖鸡饭&#xff1f;老鼠最近忙疯了……」这篇文章的评论区&#xff0c;柴油…

计算机视觉与面部识别:技术、应用与未来发展

引言 在当今数字化时代&#xff0c;计算机视觉技术迅速发展&#xff0c;成为人工智能领域的一个重要分支。计算机视觉旨在让机器理解和解释视觉信息&#xff0c;模拟人类的视觉系统。它在各行各业中发挥着重要作用&#xff0c;从自动驾驶汽车到智能监控系统&#xff0c;再到医疗…

数据库多表联查

一、内联查询 内联查询只有完全满足条件的数据才能出现的结果1.1 非等值联查 笛卡尔积&#xff0c;查到的结果具有不一致性 示例&#xff1a; select * from student,class1.2 等值查询 -- 查询出学生表和班级信息select * from student,class where student.classidclass.c…

物联网设备的画面(摄像头)嵌入到网页中,实时视频画面解决方案

一、将物联网设备的画面嵌入到网页中&#xff0c;通常有多种常见方式和解决方案。下面是一些常用的方法和技术&#xff1a; 1. 使用RTSP流 描述&#xff1a;通过RTSP协议流传输视频&#xff0c;可以通过播放器在网页中播放实时视频。解决方案&#xff1a; VLC.js&#xff1a;…