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

C++之AVL树

前言

一、AVL的概念

二、AVL树的实现

2.1 AVL的结点结构

2.2 AVL树结点的插入

平衡因子的更新

更新原则:

更新停止的条件:

插入结点以及更新平衡因子的代码实现

旋转

右单旋

左单旋

左右双旋

右左双旋

2.3 AVL树的查找

2.4 AVL树的平衡性检测

总结


前言

在上一节中,我们学习了什么是二叉搜索树,以及它的功能和结构特点。但是有一些极端情况(单叉树)会导致其查找效率大大降低,于是前人们在那个的基础上再次进行了一些约束,于是就出现了AVL树。


一、AVL的概念

AVL是最先发明的平衡二叉搜索树。它或者是一个空树,或者是一个具有如下性质的二叉搜索树:它的左右子树都是AVL树,且左右子树的高度差不超过1。AVL是一个高度平衡的二叉搜索树,它是通过控制高度差进行控制平衡的。

AVL的实现,我们要引入一个新的概念:平衡因子。平衡因子说白了就是左右子树的高度差,我们规定平衡因子的值即为右子树的高度减去左子树的高度的差。对于AVL树,我们要求平衡因子的取值范围为{0,-1,1},即左右子树的高度差不可以超过1。

我们对AVL树进行了这样一个限制就能够使得,AVL树不会出现那种极端情况了,因此AVL的查找效率是远远好于普通的二叉搜索树,其时间复杂度基本上为O(logN)。

二、AVL树的实现

AVL的由于引入了平衡因子,因此我们在实现时就要时刻注意会不会导致平衡因子超过取值范围的情况,这样就大大增加了我们的实现难度。接下来,我们就来一起看看如何去实现一个AVL树吧。

2.1 AVL的结点结构

我们实现的AVL树的结点所包含的值是一个键值对,我们主要是通过键值来对AVL的排列与查找。这与我们之前是一样的,但是我们这里引入了平衡因子,因此我们需要对双亲结点与其子节点之间的关系明确好。因此我们需要设置三个指针来表示双亲结点与它的左右孩子结点的关系。因此我们将AVL的结点设置为如下代码形式:

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):_lfet(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_bf(0){}};

2.2 AVL树结点的插入

我们插入一个值是按照二叉搜索树的规则进行插入的。我们插入结点后,只会影响我们插入结点的祖先结点的平衡因子,可能会修改新插入结点到根节点路径上的所有结点的平衡因子,也可能只修改部分祖先结点的平衡因子值。因此我们在插入操作中主要进行的就是对于平衡因子修改的操作。更新平衡因子过程中出现不平衡,对不平衡子树旋转,旋转后本质调平衡的同时,本质降低了子树 的高度,不会再影响上一层,所以插入结束。

平衡因子的更新

更新原则:
  • 平衡因子=右子树高度-左子树高度;
  • 只有子树高度的变化才会影响到当前结点的平衡因子;
  • 插入结点会影响子树的高度,如果插入在parent结点的左子树上就会使parent的平衡因子--,如果插入在parent结点的右子树上就会使parent的平衡因子++;
  • parent的子树高度是否变化决定了上面的平衡因子是否继续更新。
更新停止的条件:
  • 更新后parent的平衡因子等于0,更新中的parent的平衡因子由-1->0或者1->0,则说明在更新之前,parent左右子树是一高一低的,我们的新增结点是插入在低的那边的(因为最终左右子树的高度差相等),因此插入后的parent的左右子树的高度不变(已经达到了平衡的要求了),就不会再影响到parent的祖先结点的平衡因子,更新结束。
  • 更新后parent的平衡因子等于1或-1,更新中parent的平衡因子由0->1或者0->-1,则说明在更新之前,parent左右子树的高度是相同的,我们插入后parent的子树一边高一边低,这里虽然parent的平衡因子也满足了平衡的要求,但是对于parent的祖先结点的平衡因子造成了影响,因此我们需要继续向上更新;
  • 更新后parent的平衡因子等于2或-2,更新中parent的平衡因子由-1->-2或者1->2,则说明在更新之前,parent的所在子树就是一边高一边低的,我们将新增结点插入到高的那边就会导致高的那边更高,平衡因子相差更大,破坏了平衡。parent所在的子树不符合平衡要求,需要进行旋转处理,旋转的目标有两个:1、把parent子树旋转平衡;2、降低parent子树的高度,恢复到插入结点之前的高度。因此我们旋转后也不会再进行向上更新了;
  • 不断更新,直至到根节点,当根节点的平衡因子为1或-1时,就停止更新了。

插入结点以及更新平衡因子的代码实现

对于插入操作,和我们之前普通二叉搜索树的实现是一样的。如果它是一棵空树,我们直接new一个新的结点作为根节点,如果不是空树的话,我们需要先查找要插入的位置,然后进行插入并处理子节点与双亲结点之前的指针关系。

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){// 不平衡了,旋转处理 break;}else{assert(false);}}return true;
}

旋转

旋转的原则:我们要保证最基本的,遵循搜索树的规则,通过旋转让不平衡的树变平衡,从而降低子树的高度差。

旋转总共分为四种:左单旋,右单旋,左右双旋,右左双旋。

右单旋
  • 适用于当一个节点的左子树较高,导致平衡因子为2的情况。
  • 将左子树的根节点提升为当前节点的父节点,当前节点成为左子树根节点的右子树。

为了更加直观地展示给大家看,我们用一个实例来进行解释:下图1展示的是10为根的树,有a/b/c抽象为三棵高度为h的子树(h>=0),a/b/c均符合AVL树的要 求。10可能是整棵树的根,也可能是一个整棵树中局部的子树的根。这里a/b/c是高度为h的子树, 是一种概括抽象表示,他代表了所有右单旋的场景,实际右单旋形态有很多种,具体图2/图3/图4/ 图5进行了详细描述。如下图所示,我们可以直观地看出,这个二叉搜索树,明显左子树更高一点(我们规定层数越多,高度越高)

在a子树中插入一个新结点,导致a子树的高度从h变成h+1,不断向上更新平衡因子,导致10的平 衡因子从-1变成-2,10为根的树左右高度差超过1,违反平衡规则。10为根的树左边太高了,需要 往右边旋转,控制两棵树的平衡。

旋转核心步骤:因为5<b子树的值<10,于是我们就可以将b子树作为10结点的左孩子,而10结点可以作为5结点的右孩子。

情况1是我们抽象的左右子树都是一个空树,由于我们要进行旋转操作演示,于是我们就将新增结点插入到a处,那么就形成了一个单叉树,我们可以看到parent(结点值为10)的平衡因子是2,于是我们对parent结点进行一个右单旋,于是它就变成了一个右孩子结点,那个新增结点变成左孩子结点,整体的平衡因子都是正确的,这个二叉搜索树平衡。

情况2是我们抽象的左右子树是一个高度为1的子树,我们在a处插入一个新增结点之后,我们进行旋转操作,我们的最终目的是将parent(结点值为10)放到结点值为5的结点的右孩子位置上,于是我们就要将当前的右孩子上的结点移到其他位置上去,我们根据二叉搜索树的排列规则可以知道,这个结点是小于parent结点的,于是我们将其设置为parent的左孩子结点,然后我们再将parent结点及其它的子树一块放到结点值为5的右孩子位置上,这样平衡因子就都正确了。

情况3,4分别是抽象子树为高度为2,3的子树,我们可以看到上面的数据,可以排列形成的子树组合数成几何倍数增加,但是它们的旋转大致思路和我们的情况2是基本一样的,都是将parent结点进行旋转,放到它原孩子结点的孩子结点位置上。

//右单旋
void RotateR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;subL->_right = parent;parent->_parent = subL;Node* ppNode = parent->_parent;if (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = subL;}else{ppNode->_right = subL;}subL->_parent = ppNode;}parent->_bf = 0;subL->_bf = 0;
}
左单旋
  • 适用于当一个节点的右子树较高,导致平衡因子为-2的情况。
  • 将右子树的根节点提升为当前节点的父节点,当前节点成为右子树根节点的左子树。

下图1展示的是10为根的树,有a/b/c抽象为三棵高度为h的子树(h>=0),a/b/c均符合AVL树的要 求。10可能是整棵树的根,也可能是⼀个整棵树中局部的子树的根。这里a/b/c是高度为h的子树, 是一种概括抽象表示,他代表了所有左单旋的场景,实际左单旋形态有很多种,具体跟上面右旋类 似。

 在a子树中插入一个新结点,导致a子树的高度从h变成h+1,不断向上更新平衡因子,导致10的平 衡因子从1变成2,10为根的树左右高度差超过1,违反平衡规则。10为根的树右边太高了,需要往 左边旋转,控制两棵树的平衡。

旋转核心步骤:因为10<b子树上的值<15,因此我们可以将b子树作为10结点的右孩子(右孩子的值是大于双亲结点的值),然后我们再将10结点放到15结点的左孩子位置上。

这个左单旋和右单旋十分相似,也是有很多情况的,于是我们就将其抽象为几个子树,我们可以通过对抽象事物进行具体操作于是就是我们的常规操作了。我们可以观察上图,我们发现这个二叉搜索树是一个右边高的二叉搜索树,于是为了平衡,我们将10作为旋转点进行左旋,之后我们将其放到我们的15结点的左孩子位置上,原来15结点的左孩子就将其放到10结点的右孩子结点位置上,这样平衡因子就都正确了。

	//左单旋void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;subR->_left = parent;parent->_parent = subR;Node* ppNode = parent->_parent;if (parent == _root){_root = subR;subR->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = subR;}else{ppNode->_right = subR;}subR->_parent = ppNode;}parent->_bf = 0;subR->_bf = 0;}

左右双旋
  • 适用于当一个节点的左子树的右子树较高,导致平衡因子为2的情况。
  • 先对左子树进行左旋,再对当前节点进行右旋。

通过下面两图可以看到,左边高时,如果插入位置不是在a子树,而是插入在b子树,b子树高度从h变成h+1,引发旋转,右单旋无法解决问题,右单旋后,我们的树依旧不平衡。右单旋解决的纯粹的左边高,但是插入在b子树中,10为跟的子树不再是单纯的左边高,对于10是左边高,但是对于5是右边高,需要用两次旋转才能解决,以5为旋转点进行⼀个左单旋,以10为旋转点进行⼀个右单旋,这棵树这棵树就平衡了。

  • 上面两图分别为左右双旋中h==0和h==1具体场景分析,下面我们将a/b/c子树抽象为高度为h的AVL子树进行分析,另外我们需要把b子树的细节进⼀步展开为8和左子树高度为h-1的e和f子树,因为 我们要对b的父亲5为旋转点进行左单旋,左单旋需要动b树中的左子树。b子树中新增结点的位置不同,平衡因子更新的细节也不同,通过观察8的平衡因子不同,这里我们要分三个场景讨论。
  •  场景1:h>=1时,新增结点插入在e子树,e子树高度从h-1并为h并不断更新8->5->10平衡因子, 引发旋转,其中8的平衡因子为-1,旋转后8和5平衡因子为0,10平衡因子为1。
  •  场景2:h>=1时,新增结点插入在f子树,f子树高度从h-1变为h并不断更新8->5->10平衡因子,引发旋转,其中8的平衡因子为1,旋转后8和10平衡因子为0,5平衡因子为-1。
  • 场景3:h==0时,a/b/c都是空树,b自己就是⼀个新增结点,不断更新5->10平衡因子,引发旋转,其中8的平衡因子为0,旋转后8和10和5平衡因子均为0。

我们看上面的图片,可以清楚的看到,我们将b子树又分成了两个小子树,然后我们对插入子树的位置进行一个讨论,因为我们后面要将这个两个小子树分别放到对应的位置(旋转后的),由于这两个小子树是由原来那个高度为h的子树分成的,因此高度减1,这两个子树的高度为h-1,于是我们在这两个子树上进行插入新结点的话,插入后的高度最多为h,因此这样与a,c子树(高度为h)比较,平衡因子一定是在可取范围内。

	//左右双旋void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);if (bf == 0){subL->_bf = 0;parent->_bf = 0;subLR->_bf = 0;}else if (bf == -1){subL->_bf = 0;parent->_bf = 1;subLR->_bf = 0;}else if (bf == 1){subL->_bf = -1;parent->_bf = 0;subLR->_bf = 0;}else{assert(false);}}
右左双旋
  • 适用于当一个节点的右子树的左子树较高,导致平衡因子为-2的情况。
  • 先对右子树进行右旋,再对当前节点进行左旋。

跟左右双旋类似,下⾯我们将a/b/c⼦树抽象为⾼度h的AVL⼦树进⾏分析,另外我们需要把b⼦树的 细节进⼀步展开为12和左⼦树⾼度为h-1的e和f⼦树,因为我们要对b的⽗亲15为旋转点进⾏右单 旋,右单旋需要动b树中的右⼦树。b⼦树中新增结点的位置不同,平衡因⼦更新的细节也不同,通 过观察12的平衡因⼦不同,这⾥我们要分三个场景讨论。

  • 场景1:h>=1时,新增结点插入在e子树,e子树高度从h-1变为h并不断更新12->15->10平衡因 子,引发旋转,其中12的平衡因子为-1,旋转后10和12平衡因子为0,15平衡因子为1。
  • 场景2:h>=1时,新增结点插⼊在f子树,f子树高度从h-1变为h并不断更新12->15->10平衡因子, 引发旋转,其中12的平衡因子为1,旋转后15和12平衡因子为0,10平衡因子为-1。
  • 场景3:h==0时,a/b/c都是空树,b自己就是⼀个新增结点,不断更新15->10平衡因子,引发旋转,其中12的平衡因子为0,旋转后10和12和15平衡因子均为0。

	//右左双旋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;parent->_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);}}

2.3 AVL树的查找

AVL树相对于普通的二叉搜索树更加严格,但是其本质上还是一个二叉搜索树,因此在查找代码上它们基本上是一样的。

	Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_kv.first < kv.first){cur = cur->_right;}else if (cur->_kv.first << > kv.first){cur = cur->_left;}else{return cur;}}return nullptr;}

2.4 AVL树的平衡性检测

对于AVL的平衡性检测即对其的平衡因子或者高度差进行检查。而平衡因子的定义式为右子树高度减去左子树高度,于是我们可以显示求出左右子树高度差,然后再判断是否在平衡的高度差范围内。

	bool _IsBalanceTree(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 _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);}int _Height(Node* root){if (_root == nullptr){return 0;}int rightHeight = _Height(root->_right);int leftHeight = _Height(root->_left);return rightHeight > leftHeight ? rightHeight + 1 : leftHeight + 1;}

最后我再来附上一组用来测试是否是AVL树的测试代码

// 测试代码 
void TestAVLTree1()
{AVLTree<int, int> t;// 常规的测试⽤例 //int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };// 特殊的带有双旋场景的测试⽤例 int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };for (auto e : a){t.Insert({ e, e });}t.InOrder();cout << t.IsBalanceTree() << endl;
}
// 插⼊⼀堆随机值,测试平衡,顺便测试⼀下⾼度和性能等 
void TestAVLTree2()
{const int N = 100000;vector<int> v;v.reserve(N);srand(time(0));for (size_t i = 0; i < N; i++){v.push_back(rand()+i);}
size_t begin2 = clock();AVLTree<int, int> t;for (auto e : v){t.Insert(make_pair(e, e));}size_t end2 = clock();cout << "Insert:" << end2 - begin2 << endl;cout << t.IsBalanceTree() << endl;cout << "Height:" << t.Height() << endl;cout << "Size:" << t.Size() << endl;size_t begin1 = clock();// 确定在的值 /*for (auto e : v){t.Find(e);}*/// 随机值 for (size_t i = 0; i < N; i++){t.Find((rand() + i));}size_t end1 = clock();cout << "Find:" << end1 - begin1 << endl;
}

总结

这节我们学习的AVL就是我们之前所学习的二叉搜索树的一个进阶版,我们加上了平衡因子这一限制条件,使得二叉搜索树的查找效率得到了本质上的提升。而对于平衡的调整,主要在旋转这一操作上。对于旋转,就从我个人学习后一些经验总结:

对于左单旋是在一棵二叉树是一个单纯的右边高的情况;而对于右单旋是在一棵二叉树是一个单纯的左边高的情况。对于双旋,我们有时可能会在指针的定义上会弄混,我发现左右双旋的话,我们就要定义一个左孩子结点指针,然后再定义左孩子结点的右孩子结点指针,这种旋转是针对于那种不是纯粹的左边高的二叉树,是那种左子树中右子树高的二叉树;对于右左双旋的话,我们就要定义一个右孩子结点指针,然后再定义右孩子结点的左孩子结点指针,这种旋转是针对于那种不是纯粹的右边高的二叉树,而是那种右子树中左子树高的二叉树。以上是我记忆旋转代码的一些小技巧,不过总而言之,我们要理解其本质,那样才能更好地写好代码。

相关文章:

C++之AVL树

前言 一、AVL的概念 二、AVL树的实现 2.1 AVL的结点结构 2.2 AVL树结点的插入 平衡因子的更新 更新原则&#xff1a; 更新停止的条件&#xff1a; 插入结点以及更新平衡因子的代码实现 旋转 右单旋 左单旋 左右双旋 右左双旋 2.3 AVL树的查找 2.4 AVL树的平衡性检测 总结 前言 …...

解决STM32H743单片机USB_HOST+FATF操作usb文件

前缀 花了两天的时间整理了一下在使用STM32H743单片机开发usb相关功能时遇到的问题及解决方案&#xff0c;具体为以下2种情况&#xff1a; 1.USB插上单片机后&#xff0c;单片机卡死&#xff0c;导致长时间没有喂狗程序重启&#xff1b; 2.USB正常插拔后&#xff0c;使用FAT…...

数据结构|并查集

Hello &#xff01;朋友们&#xff0c;这是我在学习过程中梳理的笔记&#xff0c;以作以后复习回顾&#xff0c;有时略有潦草&#xff0c;一些话是我用自己的话描述的&#xff0c;可能不够准确&#xff0c;还是感谢大家的阅读&#xff01; 目录 一、并查集Quickfind 二、两种算…...

从拒绝采样到强化学习,大语言模型推理极简新路径!

大语言模型&#xff08;LLMs&#xff09;的推理能力是当下研究热点&#xff0c;强化学习在其复杂推理任务微调中广泛应用。这篇论文深入剖析了相关算法&#xff0c;发现简单的拒绝采样基线方法表现惊人&#xff0c;还提出了新算法。快来一探究竟&#xff0c;看看这些发现如何颠…...

数据中心电能质量问题解决方案及经典案例

行业背景与挑战 数据中心作为互联网的核心枢纽&#xff0c;承载着海量数据存储、计算及通信任务&#xff0c;其内部精密设备&#xff08;如恒温恒湿空调、高精度开关电源等&#xff09;对电能质量极为敏感。微小的电压波动或频率偏差可能导致设备损坏&#xff0c;而瞬态过电压…...

【软考-高级】【信息系统项目管理师】【论文基础】沟通管理过程输入输出及工具技术的使用方法

沟通管理概念 沟通是人们分享信息、思想和情感的过程&#xff0c;沟通的主旨在于互动双方建立彼此相互了解的关系&#xff0c;相互回应&#xff0c;并期待能经由沟通的过程相互接纳并达成共识。 沟通失败是很多IT项目失败的重要原因。 与IT项目成功有关的最重要的四个因素是…...

优化PCB Via Stub系列(1):一次学会利用层叠设计降低Via Stub损耗

开路谐振对SI而言真不是个好东西&#xff0c;这种1/4波长谐振会带来讯号的驻波&#xff0c;进而降低整体通道带宽&#xff0c;导致SI不佳&#xff01; 在高速PCB设计中&#xff0c;最常发生的1/4波长谐振就属过孔的Via stub&#xff0c;这个小小的金属残段可以酿成大大的SI问题…...

STP端口状态变迁及故障拓扑变化

STP端口状态变迁及故障拓扑变化 一、STP 端口状态变迁&#xff08;以标准 STP 为例&#xff0c;共 5 种状态&#xff09; 状态功能描述能否收发数据帧能否收发 BPDU持续时间进入条件Disabled端口物理关闭或被管理员手动关闭&#xff0c;不参与 STP 运算。否否-端口物理 down …...

9.idea中创建springboot项目_jdk1.8

9. idea中创建springboot项目_jdk1.8 步骤 1&#xff1a;打开 IntelliJ IDEA 并创建新项目 启动 IntelliJ IDEA。在欢迎界面&#xff0c;点击 New Project&#xff08;或通过菜单栏 File > New > Project&#xff09;。 步骤 2&#xff1a;选择 Maven 项目类型 在左侧…...

mysql 事务中如果有sql语句出错,会导致自动回滚吗?

CREATE TABLE name ( id int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT ID, name varchar(32) DEFAULT COMMENT 名称, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; 情况1.执行下列操作&#xff0c; 会发现新开窗口 去查询name表时&#xff0c;整个事务都…...

考OCM证书前需要有OCP证书

报考OCM认证必须持有有效OCP证书。 从知识体系的构建来看&#xff0c;OCP 和 OCM 认证构成了一个循序渐进的学习和考核体系。OCP 认证侧重于考察数据库管理员和开发人员对 Oracle 数据库的基础架构、日常管理、性能优化、备份恢复等核心技能的掌握。通过 OCP 考试&#xff0c;意…...

动态图表 -- eg1

问题&#xff1a; 前端vue&#xff0c;后端springboot&#xff0c;实现动态表格样式&#xff0c;&#xff08;表格List<Student>&#xff0c;Student类有年级&#xff0c;班级&#xff0c;文理科分类&#xff0c;姓名&#xff0c;学号&#xff0c;等属性。先根据年级分类…...

echo 1 > /proc/sys/kernel/nmi_watchdog报错

报错内容 /proc/sys/kernel/nmi_watchdog报错,内容如下: [root@localhost log]# echo 1 > /proc/sys/kernel/nmi_watchdog -bash: echo: write error: Unknown error 524 [root@localhost log]#报错原因 内核未配置 NMI 支持 某些自定义内核可能未编译 NMI Watchdog 驱…...

upload-labs PASS 1-5通关

PASS-01 前端javascript检查 1&#xff0c;第一个提示javascript对上传的文件进行审查 2&#xff0c;javascript工作在前端页面&#xff0c;可以直接删除具有审查功能的代码 3&#xff0c;删除之后再上传一句话木马 上传成功&#xff0c;可以使用蚁剑进行连接&#xff0c;控制网…...

大数据测试集群环境部署

Hadoop大数据集群搭建&#xff08;超详细&#xff09;_hadoop_小飞飞519-GitCode 开源社区 hadoop集群一之虚拟机安装(mac)_hadoop_皮皮虾不皮呀-华为开发者空间 hadoop集群二之hadoop安装_hadoop_皮皮虾不皮呀-华为开发者空间 虚拟机如何查看gateway | PingCode智库...

BUUCTF——Online Tool

BUUCTF——Online Tool 进入靶场 <?phpif (isset($_SERVER[HTTP_X_FORWARDED_FOR])) {$_SERVER[REMOTE_ADDR] $_SERVER[HTTP_X_FORWARDED_FOR]; }if(!isset($_GET[host])) {highlight_file(__FILE__); } else {$host $_GET[host];$host escapeshellarg($host);$host e…...

人工智能数学基础(三):微积分初步

微积分作为数学的重要分支&#xff0c;为人工智能的发展提供了坚实的理论基础。从理解数据的变化趋势到优化模型参数&#xff0c;微积分的应用贯穿其中。本文将深入探讨微积分的核心概念&#xff0c;并结合 Python 编程实例&#xff0c;助力大家轻松掌握这些关键知识点。资源绑…...

【11408学习记录】考研英语语法核心:倒装句考点全解+真题演练

倒装句 英语语法总结——特殊句式倒装全部倒装介词短语形容词副词There be 部分倒装否定副词或词组位于句首only位于句首虚拟条件句省略if 每日一句词汇第一步&#xff1a;找谓语第二步&#xff1a;断句第三步&#xff1a;简化主句定语从句 英语 语法总结——特殊句式 倒装 …...

云数据中心整体规划方案PPT(113页)

1. 引言 概述&#xff1a;云数据中心整体规划方案旨在构建弹性、高效的云计算基础设施&#xff0c;通过软件定义数据中心&#xff08;SDDC&#xff09;实现资源虚拟化与管理自动化。 2. 技术趋势与背景 技术革新&#xff1a;随着云计算、虚拟化及自动化技术的发展&#xff0c…...

java练习4

创建类对象&#xff0c;要求写一个人的类&#xff0c;内容包括&#xff1a; 值&#xff1a;年龄&#xff0c;姓名&#xff0c;家庭身份 函数&#xff1a;年龄&#xff0c;姓名修改&#xff0c;家庭身份修改&#xff0c;生孩子 package a01_第一次练习.a04_创建类对象;public cl…...

在VMware上创建Ubuntu虚拟机,与Xshell和Xftp的连接和使用

一、在VMware创建Ubuntu虚拟机 1、创建新的虚拟机 2、新建虚拟机安装导向 1&#xff09;自定义安装 2&#xff09;稍后安装操作系统 3&#xff09;选择Linux和Ubuntu64 4&#xff09;可自定义虚拟机名称和虚拟机位置 5&#xff09;选择合适的处理器数量 6&#xff09;虚拟机…...

Java常用注解通俗解释

注解就像是给Java代码贴的"便利贴"&#xff0c;它们不会改变代码本身的逻辑&#xff0c;但能给编译器、开发工具或运行时环境提供额外信息。下面我用最通俗的方式解释Java中最常用的注解&#xff1a; 一、基础篇&#xff1a;人人必知的注解 1. Override - "我…...

前端性能优化2:结合HTTPS与最佳实践,全面优化你的网站性能

点亮极速体验&#xff1a;结合HTTPS与最佳实践&#xff0c;为你详解网站性能优化的道与术 在如今这个信息爆炸、用户耐心极其有限的数字时代&#xff0c;网站的性能早已不是一个可选项&#xff0c;而是关乎生存和发展的核心竞争力。一个迟缓的网站&#xff0c;无异于在数字世界…...

小刚说C语言刷题——1032分糖果

1.题目描述 某幼儿园里&#xff0c;有 5 个小朋友编号为 1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5&#xff0c;他们按自己的编号顺序围坐在一张圆桌旁。他们身上都有若干个糖果&#xff0c;现在他们做一个分糖果游戏。 从 1 号小朋友开始&#xff0c;将他的糖…...

socket套接字-UDP(下)

socket套接字-UDP&#xff08;中&#xff09;https://blog.csdn.net/Small_entreprene/article/details/147567115?fromshareblogdetail&sharetypeblogdetail&sharerId147567115&sharereferPC&sharesourceSmall_entreprene&sharefromfrom_link在我之前搭建…...

使用Docker操作MySQL

在Docker中操作MySQL可以简化数据库的部署和管理过程。以下是详细的步骤&#xff0c;包括如何拉取MySQL镜像、创建容器以及配置远程访问权限。 拉取MySQL镜像 首先&#xff0c;使用以下命令从Docker Hub拉取MySQL镜像&#xff1a; docker pull mysql你也可以指定版本&#x…...

OpenGL ES 3.0 第二章总结:你好,三角形(Hello Triangle)

—— 从“画出第一个三角形”理解现代图形渲染流程 &#x1f530; 写在前面 OpenGL 是一个状态机型的图形 API。第二章《你好&#xff0c;三角形》是整个图形开发的起点&#xff0c;它帮助我们掌握从「准备绘制数据」到「渲染出第一个像素」的完整流程。 这一章最核心的任务是…...

neo4j vs python

1.将库中已经存在的两个节点&#xff0c;创建关系。 查询库中只有2个独立的节点。 方式一&#xff0c;python&#xff0c;使用py2neo库 #coding:utf-8 from py2neo import Graph,Node,Relationship,NodeMatcher##连接neo4j数据库&#xff0c;输入地址、用户名、密码 graph G…...

MIT6.S081-lab7前置

MIT6.S081-lab7前置 这部分包含了设备中断和锁的内容 设备中断 之前系统调用的时候提过 usertrap &#xff0c;而我们的设备中断&#xff0c;比如计时器中断也会在这里执行&#xff0c;我们可以看看具体的逻辑&#xff1a; void usertrap(void) {int which_dev 0;if((r_sst…...

通过漂移-扩散仿真研究钙钛矿-硅叠层太阳能电池中的电流匹配和滞后行为

引言 卤化物钙钛矿作为光活性半导体的出现&#xff0c;为光伏技术的发展开辟了令人振奋的新方向。[1] 除了在单结太阳能电池中的优异表现&#xff0c;目前研究的重点在于将钙钛矿吸收层整合到叠层器件中。在硅-钙钛矿叠层太阳能电池中&#xff0c;将高效的钙钛矿吸收层与成熟的…...

IIC小记

SCL 时钟同步线&#xff0c;由主机发出。 当SCL为高电平&#xff08;逻辑1&#xff09;时是工作状态&#xff0c;低电平&#xff08;逻辑0&#xff09;时是休息状态。SCL可以控制通信的速度。 SDA 数据收发线 应答位&#xff1a;前八个工作区间是一个字节&#xff0c;在SCL…...

使用 ECharts 在 Vue3 中柱状图的完整配置解析

一、初始化图表实例 const chart echarts.init(chartRef.value);二、Tooltip 提示配置 tooltip: {trigger: axis,axisPointer: {type: line // 支持 line 或 shadow 类型&#xff0c;指示器样式},backgroundColor: rgba(0,0,0,0.7),textStyle: { color: #fff },formatter: {…...

Ubuntu实现远程文件传输

目录 安装 FileZillaUbuntu 配套设置实现文件传输 在Ubuntu系统中&#xff0c;实现远程文件传输的方法有多种&#xff0c;常见的包括使用SSH&#xff08;Secure Shell&#xff09;的SCP&#xff08;Secure Copy Protocol&#xff09;命令、SFTP&#xff08;SSH File Transfer P…...

AI驱动软件工程:SoftEngine 方法论与 Lynx 平台实践分析

引言 在过去数十年中&#xff0c;软件开发领域历经了从瀑布模型到敏捷开发&#xff0c;再到DevOps的深刻变革。然而&#xff0c;面对当今快速变化的市场需求和复杂的软件系统&#xff0c;这些方法仍然显露出明显的局限性。近年来&#xff0c;基于大语言模型&#xff08;LLM&am…...

Vue基础(一) 基础用法

1.取消生产提示 Vue.config.productionTip false; Vue.config.devtools true; //运行开发调试 2.hello小案例 需要注意如下几点&#xff1a; 1.必须要有一个模板&#xff0c;其实就是一个html组件 2.新建一个Vue实例&#xff0c;并且通过el与容器建立绑定关系&#xff0…...

文心一言开发指南08——千帆大模型平台推理服务API

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 推理服务API概述 百度智能云千帆平台提供了丰富的推理服务API&#xff0c;包括对话Chat、续写Completions、向量Embeddings、批量预测等API能力。 对话Chat&#xff1a;支…...

矩阵区域和 --- 前缀和

目录 一&#xff1a;题目 二&#xff1a;算法原理 三&#xff1a;代码 一&#xff1a;题目 题目链接&#xff1a;1314. 矩阵区域和 - 力扣&#xff08;LeetCode&#xff09; 二&#xff1a;算法原理 三&#xff1a;代码 class Solution { public:vector<vector<int…...

全局id生成器生产方案

1.只要求不重复版本&#xff08;常用于分布式确定一个实体的id&#xff09; uuid&#xff08; MAC 地址、时间戳、名字空间&#xff08;Namespace&#xff09;、随机或伪随机数、时序等元素&#xff0c;计算机基于这些规则生成的 UUID 是肯定不会重复的。&#xff09; UUID 作…...

DES与AES算法深度解析:原理、流程与实现细节

DES与AES算法深度解析&#xff1a;原理、流程与实现细节 1. DES算法详解 1.1 算法架构 DES采用16轮Feistel网络结构&#xff0c;核心处理流程如下&#xff1a; 输入64位明文 → IP初始置换 → 16轮迭代处理 → 左右交换 → IP⁻末置换 → 输出64位密文 1.2 核心处理流程 …...

大厂Java面试深度解析:Dubbo服务治理、WebSocket实时通信、RESTEasy自定义注解与C3P0连接池配置实践

第一轮基础问答 面试官&#xff1a;请解释Dubbo服务注册发现的完整流程&#xff0c;以及Sentinel如何实现流量控制&#xff1f; xbhog&#xff1a;Dubbo通过Registry协议将服务地址注册到ZooKeeper&#xff0c;消费者订阅服务节点变更。Sentinel通过ResourceRegistry注册资源…...

【Qt】Qt换肤,使用QResource动态加载资源文件

【Qt】使用QResource动态加载资源文件 0.前言 对于简单的应用&#xff0c;我们可以直接读取 QSS 样式表文件来实现换肤。但一般样式里还带有图片等资源的路径&#xff0c;如果通过相对路径来加载&#xff0c;不便于管理&#xff0c;不过好处是替换图片方便。我们也可以使用 Q…...

五种机器学习方法深度比较与案例实现(以手写数字识别为例)

正如人们有各种各样的学习方法一样&#xff0c;机器学习也有多种学习方法。若按学习时所用的方法进行分类&#xff0c;则机器学习可分为机械式学习、指导式学习、示例学习、类比学习、解释学习等。这是温斯顿在1977年提出的一种分类方法。 有关机器学习的基本概念&#xff0c;…...

【18】爬虫神器 Pyppeteer 的使用

目录 一、Pyppeteer 介绍 二、安装库 三、快速上手 Python爬虫案例 | Scrape Center 在前面我们学习了 Selenium 的基本用法&#xff0c;它功能的确非常强大&#xff0c;但很多时候我们会发现 Selenium 有一些不太方便的地方&#xff0c;比如环境的配置&#xff0c;得安装好…...

封装js方法 构建树结构和扁平化树结构

在JavaScript中&#xff0c;构建树结构和将树结构扁平化是常见的操作。下面我将提供两个方法&#xff0c;一个用于从扁平化的数据中构建树结构&#xff0c;另一个用于将树结构扁平化。 构建树结构 假设我们有一个扁平化的数据列表&#xff0c;每个节点对象包含id和parentId属…...

服务器和数据库哪一个更重要

在当今数字化的时代&#xff0c;服务器和数据库都是构建和运行各种应用系统的关键组成部分&#xff0c;要说哪一个更重要&#xff0c;其实很难简单地给出定论。 服务器就像是一个强大的引擎&#xff0c;为应用程序提供了稳定的运行环境和高效的计算能力。它负责接收和处理来自…...

Nginx 核心功能与 LNMP 架构部署

一、基于授权的访问控制 1.1 功能概述 Nginx 的基于授权的访问控制通过用户名和密码验证机制&#xff0c;限制用户对特定资源的访问。其实现逻辑与 Apache 类似&#xff0c;但配置更简洁&#xff0c;适用于需保护敏感目录或页面的场景&#xff08;如管理后台&#xff09;。 …...

Python程序开发,麒麟系统模拟电脑打开文件实现

在Python开发中&#xff0c;模拟电脑打开文件操作&#xff08;即用默认程序打开文件&#xff09;&#xff0c;可以使用os.system()方法或subprocess模块来执行系统命令。以下是使用os库实现模拟打开文件的代码示例&#xff1a; 使用os.system()方法 import osfile_path &quo…...

打造惊艳的渐变色下划线动画:CSS实现详解

引言&#xff1a;为什么需要动态下划线效果&#xff1f; 在现代网页设计中&#xff0c;微妙的交互效果可以显著提升用户体验。动态下划线特效作为一种常见的视觉反馈方式&#xff0c;不仅能够引导用户注意力&#xff0c;还能为页面增添活力。本文将深入解析如何使用纯CSS实现一…...

gitmodule怎么维护

目录 ci-cd脚本 豆包文档 ci-cd脚本 git submodule init git submodule update cd /var/lib/jenkins/workspace/wvp-server-Dji/wvp-server git checkout Dji2 cd /var/lib/jenkins/workspace/wvp-server-Dji/cloud-sdk git checkout master 豆包文档...

企业战略管理(设计与工程师类)-2-战略规划及管理过程-2-外部环境分析-PESTEL模型实践

PESTEL在AFI框架中的作用​ AFI 战略框架&#xff08;Analyze, Formulate, Implement——哈佛大学商学院的教授 Michael Porter&#xff09;是企业战略管理中的一个重要理论模型&#xff0c;帮助企业系统性地分析和制定战略。 作为第一阶段Analyze的第一步&#xff0c;PESTEL…...