【C++篇】AVL树的实现
前言
本篇是基于二叉搜索树写的,详情可以去看上篇【二叉搜索树】
一,AVL树的概念
(1),AVL树是一颗二叉搜索树,它是一棵空树或者是具备以下性质的二叉搜索树:它的左右子树都是AVL树,且左右子树的高度差不超过1。AVL树是一颗高度平衡二叉搜索树,通过控制高度差去控制平衡。
(2), AVL树得名于它的发明者G.M.Adelson-Velsky和E.M.Landis是两个前苏联的科学家,他们在1962 年的论⽂《Analgorithmfortheorganizationofinformation》中发表了它。
(3),AVL树的实现这里引入一个平衡因子(balabce factor)的概念,每个节点都有一个平衡因子,平衡因子等于右子树的高度减去左子树的高度,所以说AVL树的所有节点的平衡因子都是0/1/-1。
(4),AVL树整体节点数量的分布和完全二叉树类似,高度可以控制字在logN,那么增删查改的效率也可以控制在logN,,相比二叉搜索树有了本质的提升。
二,AVL树的实现
2.1,AVL树的结构
//树节点的定义 存储的时key/value结构
template<class k,class v>
struct AVLTreeNode
{pair<k, v> _kv;AVLTreeNode<k, v>* _left;AVLTreeNode<k, v>* _right;//需记录父节点,后面更新平衡因子时会使用AVLTreeNode<k, v>* _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://...//...
private://根节点Node* _root = nullptr;
};
2.2,AVL树的插入
2.2.1,插入一个值的大致过程
1,插入一个值按照二叉搜索树的规则。(比当前节点值大去右子树找,比当前节点值小去左子树找)
2,新增结点以后,只会影响祖先结点的高度,也就是可能会影响部分祖先结点的平衡因子,所以更新 从新增结点->根结点路径上的平衡因子,实际中最坏情况下要更新到根,有些情况更新到中间就可 以停止了,具体情况我们下面再详细分析。
3,更新平衡因子过程中没有出现问题,则插入结束。
4,更新平衡因子过程中出现不平衡,对不平衡子树旋转,旋转后调平衡的同时,本质降低了子树 的高度,不会再影响上一层,所以插入结束。
2.2.2,平衡因子的更新
更新原则:
(1),平衡因子=右子树高度-左子树高度
(2),只有子树⾼度变化才会影响当前结点平衡因子。
(3),插子结点,会增加高度,所以新增结点在parent的右子树,parent的平衡因子++,新增结点在parent的左子树,parent平衡因子--。
(4),parent所在子树的高度是否变化决定了是否会继续往上更新。
更新停止条件:
一共有3种情况:
(1),插入后parent的平衡因子更新为0,说明parent的平衡因子是由1->0或者-1->0,那么插入前parent子树是一边高一边低,插入的节点插入在parent子树低的一边,所以插入后parent子树的高度不变,不会影响parent的父节点的平衡因子,更新结束。
(2),插入后parent的平衡因子更新为1或-1,说明parent的平衡因子是由0->1或者0->-1,那么插入前parent子树两边一样高,插入后parent子树一边高一边低,parent所在子树符合要求,parent子树的高度增加了1,所以会影响parent的父节点的平衡因子,需要继续向上更新。
(3),插入后parent的平衡因子更新为2或者-2,说明parent的平衡因子是由1->2或者-1->-2,那么插入前parent子树是一边高一边低,插入的节点插入在子树高的一边,导致高的那边更高了,破坏了平衡,parent所在子树不符合平衡要求,需进行旋转处理。
旋转处理后的子树:parent所在子树已经平衡,降低了parent所在子树的高度,所以旋转后不需要再向上更新平衡因子。
2.2.3,插入节点及平衡因子更新的代码实现
bool Insert(const pair<k, v>& kv)
{if (_root == nullptr){_root = new Node(kv);return true;}//找插入的位置并记录父节点Node* cur = _root;Node* parent = nullptr;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;elseparent->_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){//旋转处理break;}else{//防止更新平衡因子过程中出错assert(false);}}return true;}
2.3,旋转
2.3.1,旋转的原则:
(1),保持AVL树的规则。
(2),让旋转的树从不满足变平衡,其次降低旋转树的高度。
旋转总共分为四种,左单旋/右单旋/左右双旋/右左双旋。
2.3.2,右单旋
1,如下图,展示的是一颗以10为根的子树,10可能是是整棵树的根,也可能只是局部的子树的根。a/b/c是三颗抽象的子树,它们都满足AVL树,他们的高度h>=0.
2,在a子树中插入⼀个新结点,导致a子树的高度从h变成h+1,不断向上更新平衡因子,导致10的平衡因子从-1变成-2,10为根的树左右高度差超过1,违反平衡规则。10为根的树左边太⾼了,需要往右边旋转,控制两棵树的平衡。
3,旋转核心步骤,因为5<b子树的值<10,将b变成10的左子树,10变成5的右子树,5变成这棵树新的根,符合搜索树的规则。控制了平衡,同时这棵树的高度恢复到了插入之前的h+2。如果插入之前10是整棵树的根,那么旋转后5成为新的根;如果插入之前10是局部子树的根,那么旋转后5成为新的根,需要和上层连接起来。
示例:
2.3.3,右单旋的代码
//右单旋 纯粹的左边高void RotateR(Node* parent){//以parent为旋转点进行旋转Node* subL = parent->_left;Node* subLR = subL->_right;//记录parent的父节点Node* pparent = parent->_parent;parent->_left = subLR;if (subLR)subLR->_parent = parent;subL->_right = parent;parent->_parent = subL;//parent为整棵树的根if (parent == _root){_root=subL;_root->_parent = nullptr;}//parent为局部子树的根,需要连接上一层pparentelse{if (pparent->_left == parent)pparent->_left = subL;elsepparent->_right = subL;subL->_parent = pparent;}//旋转结束后,更新平衡因子parent->_bf = subL->_bf = 0;}
2.3.4,左单旋
左单旋和右单旋类似:
1,下图所示,10可能是是整棵树的根,也可能只是局部的子树的根。a/b/c是三颗抽象的子树,它们都满足AVL树,他们的高度h>=0.
2,在a子树中插入一个新节点,导致a子树的高度从h变为h+1,不断向上更新平衡因子,导致10的平衡因子变为2,10为根的左右子树高度差超过1,破坏了平衡。10为根的子树右边太高了,需要往左边旋转,控制两棵树的平衡。
3,旋转核心步骤,因为10<b子树的值<15,将b变成10的右子树,10变成15的左子树,15变成这棵树新的根,符合搜索树的规则,控制了平衡。同时这棵树的高度恢复到了插入之前的h+2,如果插入之前10是整棵树的根,那么旋转后5成为新的根;如果插入之前10是局部子树的根,那么旋转后5成为新的根,需要和上层连接起来。
2.3.5,左单旋的代码
//左单旋
void RotateL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;Node* pparent = parent->_parent;parent->_right = subRL;if (subRL)subRL->_parent = parent;subR->_left = parent;parent->_parent = subR;if (parent == _root){_root = subR;_root->_parent = nullptr;}else{if (pparent->_left == parent)pparent->_left = subR;elsepparent->_right = subR;subR->_parent = pparent;}parent->_bf = subR->_bf = 0;
}
2.3.6,左右双旋
前面我们讲的左单旋和右单旋都是纯粹的一边高,下面的双旋与之相反
1,观察下图,当插入的位置在b子树而不是在a子树时,此时的单旋就不能解决问题了。b子树高度从h变成h+1,引发旋转,右单旋无法解决问题,右单旋后,我们的树依旧不平衡。
可以看到,上面的情况经过单旋,结果还是不能解决问题;就第二张图而言,插入新节点后,以10为根的子树,它的左子树高,而以5为根的子树,它的右子树高,我们可以看成,对于以10为根的子树,不是纯粹的一边高。前面我们讲的单旋的场景,都是纯粹的一边高,经过单旋后,可以降低它的高度,控制平衡。
2,对于这种不是纯粹的一边高的情况,我们就需要进行双旋;
这样就解决了问题,控制了平衡。
3,以上只是两个例子,下面我们看抽象图。
4,旋转完成后,接下来是平衡因子的更新
通过上图可以发现,我们只需更新parent,subL,subLR的平衡因子,旋转后的平衡因子取决与旋转前subLR的平衡因子。
上图只反映了两种情况,旋转前,subLR的平衡因子等于1或者-1两种情况,还有一种情况是等于0,也就是一开始举的那个例子。
可以发现旋转后的平衡因子都为0.
2.3.7,左右双旋的代码
//左右双旋
void RotateLR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;//记录旋转前subLR的平衡因子int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);if (bf == 0){subLR->_bf = 0;subL->_bf = 0;parent->_bf = 0;}else if (bf == 1){subLR->_bf = 0;subL->_bf = -1;parent->_bf = 0;}else if (bf == -1){subLR->_bf = 0;subL-> _bf = 0;parent->_bf = 1;}else{assert(false);}
}
2.3.8,右左双旋
和左右双旋类似:
平衡因子的更新依旧取决于subRL的平衡因子。
2.3.9,右左双旋的代码
//右左双旋
void RotateRL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);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);}
}
2.4,AVL树平衡检测
我们实现的AVL树是否合格,我们通过检查左右子树高度差的的程序进行反向验证,同时检查一下节点的平衡因子更新是否出现了问题。
第一种方式,是一种类似前序遍历的方式:
这种方式在计算高度的时候,会重复计算
//计算高度
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 _isbalance(Node* root)
{if (root == nullptr)return true;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);int diff = rightHeight - leftHeight;if (abs(diff) >= 2){cout << root->_kv.first << "节点平衡因子异常" << endl;return false;}if (root->_bf != diff){cout << root->_kv.first << "节点平衡因子不符合实际" << endl;return false;}return _isbalance(root->_left) && _isbalance(root->_right);
}
第二种方式是采用后序遍历的方式:
//后序
bool _isbalance2(Node* root,int& height)
{if (root == nullptr){height = 0;return true;}int leftHeight = 0;if (_isbalance2(root->_left, leftHeight) == false){cout << root->_kv.first << "平衡因子异常" << endl;return false;}int rightHeight = 0;if (_isbalance2(root->_right, rightHeight) == false){cout << root->_kv.first << "平衡因子异常" << endl;return false;}height = max(leftHeight, rightHeight) + 1;return abs(rightHeight - leftHeight) < 2;
}
2.5,整体代码
#define _CRT_SECURE_NO_WARNINGS 1#include <iostream>
#include <assert.h>
using namespace std;//树节点的定义 存储的时key/value结构
template<class k,class v>
struct AVLTreeNode
{pair<k, v> _kv;AVLTreeNode<k, v>* _left;AVLTreeNode<k, v>* _right;//需记录父节点,后面更新平衡因子时会使用AVLTreeNode<k, v>* _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:bool Insert(const pair<k, v>& kv){if (_root == nullptr){_root = new Node(kv);return true;}//找插入的位置并记录父节点Node* cur = _root;Node* parent = nullptr;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;elseparent->_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 && 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);}else if (parent->_bf == 2 && cur->_bf == -1){RotateRL(parent);}else{assert(false);}break;}else{assert(false);}}return true;}//右单旋 纯粹的左边高void RotateR(Node* parent){//以parent为旋转点进行旋转Node* subL = parent->_left;Node* subLR = subL->_right;//记录parent的父节点Node* pparent = parent->_parent;parent->_left = subLR;if (subLR)subLR->_parent = parent;subL->_right = parent;parent->_parent = subL;//parent为整棵树的根if (parent == _root){_root=subL;_root->_parent = nullptr;}//parent为局部子树的根,需要连接上一层pparentelse{if (pparent->_left == parent)pparent->_left = subL;elsepparent->_right = subL;subL->_parent = pparent;}//旋转结束后,更新平衡因子parent->_bf = subL->_bf = 0;}//左单旋void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;Node* pparent = parent->_parent;parent->_right = subRL;if (subRL)subRL->_parent = parent;subR->_left = parent;parent->_parent = subR;if (parent == _root){_root = subR;_root->_parent = nullptr;}else{if (pparent->_left == parent)pparent->_left = subR;elsepparent->_right = subR;subR->_parent = pparent;}parent->_bf = subR->_bf = 0;}//左右双旋void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;//记录插入前subLR的平衡因子int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);if (bf == 0){subLR->_bf = 0;subL->_bf = 0;parent->_bf = 0;}else if (bf == 1){subLR->_bf = 0;subL->_bf = -1;parent->_bf = 0;}else if (bf == -1){subLR->_bf = 0;subL-> _bf = 0;parent->_bf = 1;}else{assert(false);}}//右左双旋void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);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 Inorder(){_Inorder(_root);cout << endl;}bool isbalance(){return _isbalance(_root);}bool isbalance2(){int height = 0;return _isbalance2(_root, height);}
private://后序bool _isbalance2(Node* root,int& height){if (root == nullptr){height = 0;return true;}int leftHeight = 0;if (_isbalance2(root->_left, leftHeight) == false){cout << root->_kv.first << "平衡因子异常" << endl;return false;}int rightHeight = 0;if (_isbalance2(root->_right, rightHeight) == false){cout << root->_kv.first << "平衡因子异常" << endl;return false;}height = max(leftHeight, rightHeight) + 1;return abs(rightHeight - leftHeight) < 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 _isbalance(Node* root){if (root == nullptr)return true;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);int diff = rightHeight - leftHeight;if (abs(diff) >= 2){cout << root->_kv.first << "节点平衡因子异常" << endl;return false;}if (root->_bf != diff){cout << root->_kv.first << "节点平衡因子不符合实际" << endl;return false;}return _isbalance(root->_left) && _isbalance(root->_right);}void _Inorder(Node* root){if (root == nullptr)return;_Inorder(root->_left);cout << root->_kv.first<<":" << root->_kv.second << endl;_Inorder(root->_right);}
private://根节点Node* _root = nullptr;
};
三,测试
#include "AVL-Tree.h"
#include <vector>
#include <time.h>void TestAVLTree1()
{srand(time(0));vector<int> v;int n = 10000;for (int i = 0; i < n; i++){v.push_back(rand() + i);}AVLTree<int, int> t;int begin1 = clock();for (auto e : v){t.Insert(make_pair(e, e));}int end1 = clock();cout << "insert:" << end1 - begin1 << endl;cout << t.isbalance2() << endl;
}
int main()
{TestAVLTree1();return 0;
}
相关文章:
【C++篇】AVL树的实现
前言 本篇是基于二叉搜索树写的,详情可以去看上篇【二叉搜索树】 一,AVL树的概念 (1),AVL树是一颗二叉搜索树,它是一棵空树或者是具备以下性质的二叉搜索树:它的左右子树都是AVL树ÿ…...
VIVO Android面试题及参考答案
请重写算法题:求数组的全排列。 思路: 要获取一个数组的全排列,我们可以利用回溯算法。具体来说,回溯算法通过递归的方式逐步生成排列,在每一步都将一个元素加入排列中,然后在下一步递归中排除已选元素,回溯的时候撤销选择,尝试其他可能。 步骤: 递归生成排列: 使…...
联邦大模型微调
微调(Fine-tuning)是一种迁移学习的技术,用于在一个已经预训练好的模型基础上,通过进一步训练来适应特定的任务或数据集。微调可以在具有相似特征的任务之间共享知识,从而加快训练速度并提高模型性能。 微调步骤&…...
DigitalOcean Kubernetes现已支持VPC natvie集群
DigitalOcean Kubernetes (DOKS)的VPC natvie集群功能现已正式上线!这一新功能实现了DOKS集群与虚拟私有云(VPC)资源之间的无缝集成,提升了工作负载的网络灵活性和可扩展性。 什么是VPC natvie 集群? VPC natvie 集群支…...
【每日学点鸿蒙知识】H5与C++通讯、动态参数化配置、ArkTS调用JS、Json对象转换、showToast在多次调用问题
1、HarmonyOS h5页面和C如何进行双向通讯? 前的规格是H5只能和ArkTS通讯,ArkTS通过NDK接口与C通讯,只有网络拦截有C接口。参考链接:https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/_web-V5 2、HarmonyO…...
ZC706开发板教程:使用SD卡启动工具烧写flash
在使用开发板的过程中,常常需要通过 Flash 模式启动。然而,使用 JTAG 模式在线烧写 flash 的过程既繁琐又耗时,许多用户对此表示困扰。本期将为您提供解决方案,以简化这一流程。 我们分析正常的flash烧写过程,就是通过…...
问题-01
Mybatis比较失效问题 1、问题复现 whetherPromoterNull是字符串类型,0使用单引号包裹,进行比较时发现不起作用 <if test"whetherPromoterNull ! null and whetherPromoterNull.trim() 0"> and sui.share_user_id is not null</if&g…...
内容营销专家刘鑫炜:误区四,目标不明,营销如同“盲头苍蝇”?
我们经常会遇到这样的客户,稿件提交过来后,一会儿说要发这个媒体,不一会儿又要发那个媒体,而且这两个媒体根本没有关联性,这时候,我们都会问客户,你推广这篇稿件的目的是什么,是为了…...
java基础1:处理Map
一、适用场景:相对Map排序,想获取其中最大或最小值。 1、获取map集合里,获取 max(value)对应的key 1)、方式1 Testpublic void MapInnerMaxValue() {HashMap<String, Integer> hashMap new HashMap<>();hashMap.put("a&q…...
企业销售人员培训系统|Java|SSM|VUE| 前后端分离
【技术栈】 1⃣️:架构: B/S、MVC 2⃣️:系统环境:Windowsh/Mac 3⃣️:开发环境:IDEA、JDK1.8、Maven、Mysql5.7 4⃣️:技术栈:Java、Mysql、SSM、Mybatis-Plus、VUE、jquery,html 5⃣️数据库可…...
设计模式-责任链模式
一、简介 责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,用于将请求的发送者与接收者解耦,使多个处理对象都有机会处理该请求。这些处理对象通过形成一条链式结构依次处理请求,直到某个对象能够完…...
04、Spring MVC
Spring MVC是Spring的Web模块,用来开发Web应用的,它最终作为B/S、C/S模式下的Server端 Web应用的核心就是处理HTTP请求并响应。 一、关于两种开发模式说明 我们使用Spring MVC有两个开发模式 前后分离(数据与页面分离) @ResponseBody@RestController其涉及的生要机制是:…...
K8S 黑魔法之如何从 Pod 拿到节点的命令行
搞 K8S 运维的时候,偶尔会遇到一个难题,定位到问题出在某个节点上,而由于权限审批,错误配置等等各种原因,没有办法拿到节点的 SSH 权限,无法进入节点命令行进一步排障。 这个时候,就可以用这个…...
谷歌用Anthropic的Claude帮Gemini“打磨”性能
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...
React性能优化:构建更高效的应用
在现代前端开发中,React已经成为构建复杂、交互频繁应用的首选框架。然而,随着应用规模的扩大和功能的丰富,组件的频繁重渲染可能会成为性能瓶颈,影响用户体验。为了提升React应用的性能,开发者需要掌握一系列性能优化技巧和工具。本文将详细介绍React性能优化的各个方面,…...
Linux从0到1——线程同步和互斥【互斥量/条件变量/信号量/PC模型】
Linux从0到1——线程同步和互斥 1. Linux线程互斥1.1 问题引入1.2 互斥相关概念1.3 多执行流并发访问公共资源的数据不一致问题1.4 互斥量(锁)1.5 改进抢票系统1.6 锁的简单封装1.7 锁的实现原理1.8 可重入VS线程安全1.9 死锁 2. Linux线程同步2.1 理解同…...
无人机驾驶证对入伍有帮助吗?
无人机驾驶证对入伍确实有一定的帮助,主要体现在以下几个方面: 一、提升专业技能 无人机操作是一项高度专业化的技能,需要掌握飞行原理、航电系统、任务规划、紧急处理等多方面的知识。通过考取无人机驾驶证,个人可以系统地学习这…...
【贪心算法】贪心算法七
贪心算法七 1.整数替换2.俄罗斯套娃信封问题3.可被三整除的最大和4.距离相等的条形码5.重构字符串 点赞👍👍收藏🌟🌟关注💖💖 你的支持是对我最大的鼓励,我们一起努力吧!😃…...
MySQL 锁概述
1.锁的分类 根据不同的分类角度可将锁分为: 按是否共享分:S 锁、X 锁按粒度分:表级锁、行级锁、全局锁(锁整个库)、页锁(锁数据页)意向锁:意向 S 锁、意向 X 锁:都是表…...
springboot502基于WEB的牙科诊所管理系统(论文+源码)_kaic
牙科诊所管理系统的设计与实现 摘要 近年来,信息化管理行业的不断兴起,使得人们的日常生活越来越离不开计算机和互联网技术。首先,根据收集到的用户需求分析,对设计系统有一个初步的认识与了解,确定牙科诊所管理系统的…...
overleaf中出现TeX capacity exceeded PDF object stream buffer=5000000的原因和解决方案
在插入pdf 配图后,编译出错提示信息如图,很可能的一个原因是pdf文件大小太大了,最好压缩一下,压缩到1MB以内。...
LabVIEW神经肌肉电刺激与记录系统
神经肌肉电刺激技术在康复医学和神经科学领域占有重要地位。基于LabVIEW开发了神经肌肉电刺激与记录系统,该系统具备可控电脉冲输出与高效数据采集功能,适用于临床和科研领域。 项目背景 神经肌肉电刺激技术用于治疗各类神经和肌肉系统疾病,…...
【CSS in Depth 2 精译_096】16.4:CSS 中的三维变换 + 16.5:本章小结
当前内容所在位置(可进入专栏查看其他译好的章节内容) 第五部分 添加动效 ✔️【第 16 章 变换】 ✔️ 16.1 旋转、平移、缩放与倾斜 16.1.1 变换原点的更改16.1.2 多重变换的设置16.1.3 单个变换属性的设置 16.2 变换在动效中的应用 16.2.1 放大图标&am…...
Label-Studio X SAM 半自动化标注
教程:playground/label_anything/readme_zh.md at main open-mmlab/playground GitHub B站视频:Label Studio x Segment Anything Model 半自动化标注_哔哩哔哩_bilibili 需要注意: 1.LINUX上跑比较方便 2.中文路径、文件名闯大祸 3. 4…...
Mono里运行C#脚本8—mono_image_storage_open打开EXE文件
Mono里运行C#脚本8—mono_image_storage_open打开EXE文件 前面分析哈希表的实现,以及文件打开的底层函数,还有保存到HASH表里的数据结构。 static MonoImageStorage * mono_image_storage_open (const char *fname) { char *key = NULL; key = mono_path_resolve_symlinks…...
【WebSocket】tomcat内部处理websocket的过程
websocket请求格式 浏览器请求 GET /webfin/websocket/ HTTP/1.1。 Host: localhost。 Upgrade: websocket。 Connection: Upgrade。 Sec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg。 Origin: http://服务器地址。 Sec-WebSocket-Version: 13。 服务器响应 HTTP/1.1 101 Swi…...
将一个组件的propName属性与父组件中的variable变量进行双向绑定的vue3(组件传值)
封装组件看这个,然后理解父子组件传值 应用场景: 1.使用v - model语法实现双向绑定(传值两边都不用定义方法接数据) 1.子组件 1. update:modelValue事件是MultiSelect组件对象自带的事件 2.:options"countries" opti…...
Linux下通用型shellcode的编写
实验目的及要求 目的: 通过对本实验执行过程的理解,认真分析总结,能够独立的在 Linux 下进行 shellcode 的编写。 要求: (1)%70:完成对 shellcode 的分析及提取 (2)%…...
STM32F103RCT6学习之二:GPIO开发
GPIO基础 1.简介 2.GPIO基本结构 3.种模式 GPIO基本功能 1.输出功能--LED灯闪烁 1)进行基本配置 2)编辑代码 主要在main.c中编辑。 int main(void) {/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration------------------------------------------------…...
kong网关使用pre-function插件,改写接口的返回数据
一、背景 kong作为api网关,除了反向代理后端服务外,还可对接口进行预处理。 比如本文提及的一个小功能,根据http header某个字段的值,等于多少的时候,返回一个固定的报文。 使用到的kong插件是pre-function。 除了上…...
C++、Python有哪些相同和不同
C 和 Python 是两种流行的编程语言,设计理念和应用场景各有不同,但也有一些相似之处。以下是它们在语言特性、性能、语法等方面的相同点和不同点的比较: 相同点 支持多种编程范式: 面向对象编程 (OOP):两者都支持类、继…...
Spring Boot 自动配置:从 spring.factories 到 AutoConfiguration.imports
Spring Boot 提供了强大的自动配置功能,通过约定优于配置的方式大大简化了应用开发。随着版本迭代,自动配置的实现方式也逐渐优化,从早期的 spring.factories 文件到最新的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConf…...
HarmonyOS Next 应用元服务开发-应用接续动态配置迁移按需退出
按需退出,支持应用动态选择迁移成功后是否退出迁移源端应用(默认迁移成功后退出迁移源端应用)。如果应用不想让系统自动退出迁移源端应用,则可以设置不退出,参数定义见SUPPORT_CONTINUE_SOURCE_EXIT_KEY。 示例&#x…...
SQL-leetcode-180. 连续出现的数字
180. 连续出现的数字 表:Logs -------------------- | Column Name | Type | -------------------- | id | int | | num | varchar | -------------------- 在 SQL 中,id 是该表的主键。 id 是一个自增列。 找出所有至少连续出现三次的数字。 返回的…...
使用Python pickle模块进行序列化
使用Python pickle模块进行序列化 在Python中,pickle模块是一个用于实现数据序列化与反序列化的强大工具。与json模块不同的是,pickle支持将几乎所有的Python对象进行序列化,包括字典、列表、类实例,甚至函数。这使得它在处理复杂…...
责任链模式(ChainofResponsibilityPattern)
文章目录 1.定义2.结构3.问题描述代码实现 1.定义 允许你将请求沿着处理者链进行发送。 收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。 2.结构 处理者(Handler):声明了所有具体处理者的通用接口。 该接口通常…...
自定义 Element Plus 树状表格图标
在开发使用 Element Plus 的树状表格时,默认的展开/收起图标可能不能满足设计需求。为了更符合项目要求,可以通过覆盖样式的方式来自定义这些图标。以下记录了实现自定义树状表格图标的完整过程。 实现效果 有子节点且未展开时:显示一个加号…...
Vue2:用一个例子理解一下vue template中属性前面的冒号“:“
常用写法 table中绑定数据,我们通常这么写: <el-table :data="tableData" style="width: 100%">data() {tableData:[], } data绑定变量名可变的动态对象 但是上一篇文中,因为要生成15组相似的table,它们的格式都一样,只是数据…...
AF3 partition_tensor函数源码解读
该函数将输入张量按原子维度 (n_atoms) 分块,以局部窗口方式滑动,生成 滑动窗口张量。 在神经网络中,尤其是处理大规模序列数据(例如蛋白质原子特征)时,直接对整个序列执行操作可能会导致计算和内存效率问题。partition_tensor 函数通过对输入张量进行分块(partitions)…...
C++类与对象上
1.面向过程和面向对象初步认识 C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题 例如洗衣服: C是基于面向对象的,关注的是对象,讲一件事拆分成不同的对象,靠对…...
中巨伟业推出高安全高性能32位智能卡内核可编程加密芯片SMEC88SP/ST
1、产品特性 以最高安全等级的智能卡芯片内核为基础,具有极高的软硬件安全性 实现客户关键功能或算法代码下载,用户可以灵活实现自有知识产权的保护 标准 SOP8、SOT23-6 封装形式,器件封装小 标准 I2C 接口,具有接…...
Python微博动态爬虫
本文是刘金路的《语言数据获取与分析基础》第十章的扩展,详细解释了如何利用Python进行微博爬虫,爬虫内容包括微博指定帖子的一级评论、评论时间、用户名、id、地区、点赞数。 整个过程十分明了,就是用户利用代码模拟Ajax请求,发…...
包管理工具npm、yarn、pnpm、cnpm详解
1. 包管理工具 1.1 npm # 安装 $ node 自带 npm# 基本用法 npm install package # 安装包 npm install # 安装所有依赖 npm install -g package # 全局安装 npm uninstall package # 卸载包 npm update package # 更新包 npm run script #…...
Docker和Kubernetes(K8s)区别
目录 1. Docker Docker 的核心概念: Docker 的功能: Docker 常见使用场景: 2. Kubernetes (K8s) Kubernetes 的核心概念: Kubernetes 的功能: Kubernetes 常见使用场景: 3.Docker 和 Kubernetes 的…...
龙智出席2024零跑智能汽车技术论坛,分享功能安全、需求管理、版本管理、代码扫描等DevSecOps落地实践
龙智快讯 2024年12月5日,由零跑和盖世汽车主办的“2024零跑智能汽车技术论坛”在杭州零跑总部圆满落幕。此次技术论坛聚焦AI语言大模型、AUTOSAR AP平台、DevOps、端到端自动驾驶等热点话题展开探讨,旨在推动智能汽车技术的创新与发展。 龙智作为国内领先…...
SQL进阶技巧:如何分析双重职务问题?
目录 0 背景描述 1 数据准备 2 问题分析 方法2:利用substr函数,充分利用数据特点【优秀解法】 3 小结 0 背景描述 在 CompuServe 刚成立时,Nigel Blumenthal 遇到一个应用程序中的困难。他需要获取公司人员所担任角色的源表,…...
SAQ问卷的定义,SAQ问卷是什么?
SAQ问卷,全称为可持续发展评估问卷(Sustainability Assessment Questionnaire),是一种在线自评工具,其深远意义与广泛应用在当今商业环境中愈发凸显。它不仅是一种衡量企业在环境、社会和治理(ESGÿ…...
Express.js 有哪些常用的中间件?
在使用 Express.js 开发应用程序时,中间件(Middleware)是处理请求和响应的关键组件。它们可以执行各种任务,如解析请求体、添加HTTP头部、记录日志等。以下是一些常用的中间件: body-parser 用于解析传入的请求体。它…...
K8s DaemonSet的介绍
1. 什么是 DaemonSet? DaemonSet 是 Kubernetes 中的一种控制器,用于确保每个(或某些指定的)节点上运行一个 Pod 副本。它是为部署守护进程设计的,例如需要在每个节点上运行的任务或工具。 特点: Pod 会随…...
同步异步日志系统:设计模式
设计模式是前辈们对代码开发经验的总结,是解决特定问题的⼀系列套路。它不是语法规定,⽽是⼀ 套⽤来提⾼代码可复⽤性、可维护性、可读性、稳健性以及安全性的解决⽅案。 为什么会产生设计模式这样的东西呢?就像人类历史发展会产生兵法。最开…...