AVL树和红黑树

AVL树和红黑树

  • 一、AVL树
    • 1. 概念
    • 2. 原理
      • AVL树节点的定义
      • 插入
        • 不违反AVL树性质
        • 违反AVL树性质
          • 左单旋
          • 右单旋
          • 左右双旋
          • 右左双旋
          • 总结
      • 删除
    • 3. 验证代码
    • 4. AVL树完整实现代码
  • 二、红黑树
    • 1. 概念
    • 2. 性质
    • 3. 原理
      • 红黑树节点的定义
      • 默认约定
      • 插入
        • 情况一 (u存在且为红)
        • 情况二(u不存在或u存在且为黑)
      • 删除
    • 4. 相关的验证测试代码
    • 5. 红黑树完整实现代码
  • 三、总结

一、AVL树

1. 概念

AVL树又称高度平衡二叉搜索树:

  1. 它的左右子树都是AVL树
  2. 左右子树高度之差(简称平衡因子)的绝对值不超过1(1, 0, -1)。

AVL树的增删查改的效率

2. 原理

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;   //平衡因子 -- balance factorAVLTreeNode(const pair<K, V>& kv):_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr), _bf(0){}
};

插入

  1. 按照二叉树的方式插入新节点
  2. 调节平衡因子
    a. 旋转
    b. 结束
不违反AVL树性质

不违反AVL树性质的平衡因子调整的情况:
平衡因子调整的不同情况
根据上述平衡因子的调整情况得出结论:新增节点,影响了被调整子树的高度,需要沿着祖先路径向上调节,直到调节到平衡因子为0的节点或者根节点,只要平衡因子的绝对值不超过1就符合AVL树的性质。

代码实现:

//控制平衡因子
while (parent)
{//新增节点在父节点的左,则--。在右则++if (cur == parent->_left){parent->_bf--;}else{parent->_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);}
}
违反AVL树性质

违反AVL树性质的平衡因子调整的情况:

这种情况就是平衡因子在调整的过程中出现绝对值大于1的情况 --> 需要进行旋转

左单旋

逻辑分析:
左单旋
代码实现:

void RotateL(Node* parent)
{Node* cur = parent->_right;Node* curleft = cur->_left;//重新链接parent->_right = curleft;if(curleft)curleft->_parent = parent;cur->_left = parent;//提前保存parent->_parent,可能是根节点,也可能是子树的根节点Node* ppnode = parent->_parent;parent->_parent = cur;if (ppnode == nullptr){_root = cur;cur->_parent = nullptr; }else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}parent->_bf = cur->_bf = 0;    //根据上面的分析,不平衡的AVL树,在完成旋转后只需要在这两个节点更改平衡因子
}
右单旋

逻辑分析:
右单旋
代码实现:

void RotateR(Node* parent)
{Node* cur = parent->_left;Node* curright = cur->_right;parent->_left = curright;if (curright)curright->_parent = parent;//提前保存parent->_parent,可能是根节点,也可能是子树的根节点Node* ppnode = parent->_parent;cur->_right = parent;parent->_parent = cur;if (ppnode == nullptr){_root = cur;cur->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}parent->_bf = cur->_bf = 0;
}
左右双旋

逻辑分析:
左右双旋
代码实现:

void RotateLR(Node* parent)
{Node* cur = parent->_left;Node* curright = cur->_right;int bf = curright->_bf;//在调用完成这两个函数时,改动了parent和cur的影响因子RotateL(parent->_left);RotateR(parent);//调节平衡因子if (bf == -1){parent->_bf = 1;cur->_bf = 0;curright->_bf = 0;}else if (bf == 1){parent->_bf = 0;cur->_bf = -1;curright->_bf = 0;}else if (bf == 0){parent->_bf = 0;cur->_bf = 0;curright->_bf = 0;}else{assert(false);}
}
右左双旋

逻辑分析:
右左双旋
基本原理同左右双旋
代码实现:

void RotateRL(Node* parent)
{Node* cur = parent->_right;Node* curleft = cur->_left;int bf = curleft->_bf;RotateR(parent->_right);RotateL(parent);if (bf == 1){parent->_bf = -1;cur->_bf = 0;curleft = 0;}else if (bf == -1){parent->_bf = 0;cur->_bf = 1;curleft->_bf = 0;}else if (bf == 0){parent->_bf = 0;cur->_bf = 0;curleft->_bf = 0;}else{assert(false);}
}
总结

通过parent和cur的平衡因子,来判断是怎么旋转。旋转完成后,高度降低,AVL树平衡,所以不需要继续向上更新

代码:

else if (parent->_bf == 2 || parent->_bf == -2)
{//树不平衡了,需要旋转if (parent->_bf == 2 && cur->_bf == 1){RotateL(parent);}else if (parent->_bf == -2 && cur->_bf == -1){RotateR(parent);}else if (parent->_bf == -2 && cur->_bf == 1){RotateLR(parent);}else if (parent->_bf == 2 && cur->_bf == -1){RotateRL(parent);}break;
}

删除

类似二叉搜索树的删除。这里不进行介绍

  1. 寻找删除目标(是否存在,存在的位置)
  2. 判断删除目标的结构,使用二叉搜索树的转换思路,将其转为叶子节点
  3. 将删除目标与AVL树断开,调整平衡。

3. 验证代码

验证该树是否是AVL树

代码:

//判断是不是AVLTree
bool IsBalance()
{return _IsBalance(_root);
}//树的高度
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 leftHight = Height(root->_left);int rightHight = Height(root->_right);if (rightHight - leftHight != root->_bf){cout << "平衡因子异常:" << root->_kv.first << "->" << root->_bf << endl;return false;}return abs(rightHight - leftHight) < 2 && _IsBalance(root->_left) && _IsBalance(root->_right);
}

4. 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;   //平衡因子 -- balance factorAVLTreeNode(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* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else{return false;}}//链接新增节点cur = new Node(kv);if (parent->_kv.first > kv.first){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;//控制平衡因子while (parent){//新增节点在父节点的左,则--。在右则++if (cur == parent->_left){parent->_bf--;}else{parent->_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){RotateL(parent);}else if (parent->_bf == -2 && cur->_bf == -1){RotateR(parent);}else if (parent->_bf == -2 && cur->_bf == 1){RotateLR(parent);}else if (parent->_bf == 2 && cur->_bf == -1){RotateRL(parent);}break;}else{assert(false);}}return true;}//判断是不是AVLTreebool IsBalance(){return _IsBalance(_root);}private://树的高度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 leftHight = Height(root->_left);int rightHight = Height(root->_right);if (rightHight - leftHight != root->_bf){cout << "平衡因子异常:" << root->_kv.first << "->" << root->_bf << endl;return false;}return abs(rightHight - leftHight) < 2 && _IsBalance(root->_left) && _IsBalance(root->_right);}//旋转void RotateR(Node* parent){Node* cur = parent->_left;Node* curright = cur->_right;parent->_left = curright;if (curright)curright->_parent = parent;Node* ppnode = parent->_parent;cur->_right = parent;parent->_parent = cur;if (ppnode == nullptr){_root = cur;cur->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}parent->_bf = cur->_bf = 0;}void RotateL(Node* parent){Node* cur = parent->_right;Node* curleft = cur->_left;//重新链接parent->_right = curleft;if(curleft)curleft->_parent = parent;cur->_left = parent;//提前保存parent->_parent,可能是根节点,也可能是子树的根节点Node* ppnode = parent->_parent;parent->_parent = cur;if (ppnode == nullptr){_root = cur;cur->_parent = nullptr; }else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}parent->_bf = cur->_bf = 0;}void RotateLR(Node* parent){Node* cur = parent->_left;Node* curright = cur->_right;int bf = curright->_bf;//在调用完成这两个函数时,改动了parent和cur的影响因子RotateL(parent->_left);RotateR(parent);//调节平衡因子if (bf == -1){parent->_bf = 1;cur->_bf = 0;curright->_bf = 0;}else if (bf == 1){parent->_bf = 0;cur->_bf = -1;curright->_bf = 0;}else if (bf == 0){parent->_bf = 0;cur->_bf = 0;curright->_bf = 0;}else{assert(false);}}void RotateRL(Node* parent){Node* cur = parent->_right;Node* curleft = cur->_left;int bf = curleft->_bf;RotateR(parent->_right);RotateL(parent);if (bf == 1){parent->_bf = -1;cur->_bf = 0;curleft = 0;}else if (bf == -1){parent->_bf = 0;cur->_bf = 1;curleft->_bf = 0;}else if (bf == 0){parent->_bf = 0;cur->_bf = 0;curleft->_bf = 0;}else{assert(false);}}private:Node* _root = nullptr;
};

二、红黑树

1. 概念

二叉搜索树的一种,每个节点增加一个存储位表示节点标识的颜色(Red或Black),通过每条路径的每个节点的着色方式的限制,红黑树保证没有一条路径会比其它路径长出两倍。(趋近平衡)

2. 性质

  1. 每个节点只能是红色或黑色
  2. 根节点是黑色
  3. 任何路径没有连续的节点是红色的
  4. 每条路径黑色节点的数量相等

根据性质3和4,保证了红黑树最长路径的节点个数不超过最短路径节点个数的两倍。

eg1(图):
红黑树

eg2(图):证明没有一条路径会比其它路径长出两倍
证明性质

3. 原理

红黑树节点的定义

//节点的颜色
enum Color
{RED,    //红BLACK   //黑
};template<class K, class V>
struct RBTreeNode
{RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;pair<K, V> _kv;Color _color;RBTreeNode(const pair<K, V>& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_color(RED){}
};

默认约定

新插入的节点,颜色默认为红色

  1. 不需要调整
    如果新插入节点的双亲节点的颜色为黑色,则没有违反红黑树的任何性质。
  2. 需要调整
    当新插入节点的双亲节点颜色为红色,违反了红黑树的性质3。

默认约定1:cur为当前节点,p为双亲节点,g为祖父节点,u为叔叔节点
默认约定2:被调节的树,可能是一颗完整的树,也可能是一颗子树。
eg: (a,b,c,d,e理解成子树即可)该例符合下述插入的情况一
节点的状态

插入

注意: 如果不违反红黑树规则直接寻找位置插入即可,所以这里只介绍违反规则的情况。

违反规则的情况只需要判断u节点的状态。
原因:插入一个节点受影响的节点有cur,p,g,u四个节点,但是前三个节点的颜色是固定的,cur默认是红色。p如果为黑则不违反规则,所以不需要进行调整。g如果为红则证明插入cur之前这棵树就已经不是红黑树。

情况一 (u存在且为红)
  1. 被调节的树是一颗完整的树

是一颗完整的树

  1. 被调节的树是一颗子树
    是一颗子树
情况二(u不存在或u存在且为黑)

这里只对左单旋进行介绍,右单旋情况同理
单旋

这里只对左右双旋进行介绍,右左双旋情况同理
双旋

删除

红黑树的删除原理:根据其子节点的情况进行分类讨论。

  1. 要删除的节点没有子节点,直接将其删除即可。
  2. 要删除的节点只有一个子节点,将其子节点替代要删除的节点,并将替代节点染成黑色,以保持红黑树的性质。
  3. 要删除的节点有两个子节点,需要找到其后继节点(即大于该节点值的最小节点)或前驱节点(即小于该节点值的最大节点)来替代要删除的节点。将后继节点或前驱节点的值复制到要删除的节点,并将要删除的节点指向后继节点或前驱节点。接下来,将删除后继节点或前驱节点的问题转化为删除拥有一个或没有儿子节点的情况。在删除后继节点或前驱节点时,如果删除的节点是黑色的,则可能破坏红黑树的性质。为了维持红黑树的性质,需要进行调整操作。
  4. 根据删除节点的兄弟节点的情况,通过旋转和重新着色等操作重新调整树的结构,即保持红黑树的性质。 在进行旋转和重新着色等调整操作后,最后再删除要删除的节点。

4. 相关的验证测试代码

验证该树是否是红黑树

代码:

//判断该树是不是红黑树
bool IsBalance()
{return IsBalance(_root);
}//计算红黑树的高度
int Height()
{return Height(_root);
}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 CheckColor(Node* root, int blacknum, int benchmark)
{if (root == nullptr){if (blacknum != benchmark){return false;}return true;}//计算每条路径的黑色节点if (root->_color == BLACK){++blacknum;}if (root->_color == RED && root->_parent && root->_parent->_color == RED){cout << root->_kv.first << "出现连续红色节点" << endl;return false;}return CheckColor(root->_left, blacknum, benchmark)&& CheckColor(root->_right, blacknum, benchmark);
}bool IsBalance(Node* root)
{if (root == nullptr){return true;}if (root->_color != BLACK){return false;}//基准值 -->  用于比较别的路径黑色节点个数int benchmark = 0;Node* cur = _root;while (cur){if (cur->_color == BLACK)benchmark++;cur = cur->_left;}return CheckColor(root, 0, benchmark);
}

5. 红黑树完整实现代码

//节点的颜色
enum Color
{RED,BLACK
};template<class K, class V>
struct RBTreeNode
{RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;pair<K, V> _kv;Color _color;RBTreeNode(const pair<K, V>& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_color(RED){}
};template<class K, class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public:bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);_root->_color = BLACK;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);cur->_color = RED;if (parent->_kv.first > kv.first){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;//调整   这里parent是否为空,是为了下一次循环判断//       如果parent->_color == BLACK也不用玩了while (parent && parent->_color == RED){Node* grandfather = parent->_parent;if (grandfather->_left == parent){Node* uncle = grandfather->_right;//u为红if (uncle && uncle->_color == RED){parent->_color = uncle->_color = BLACK;grandfather->_color = RED;//继续向上调整cur = grandfather;parent = cur->_parent;}else //u不存在 或 存在且为黑{if (cur == parent->_left){//      g//   p//cRotateR(grandfather);parent->_color = BLACK;grandfather->_color = RED;}else{//      g//   p//      c	RotateL(parent);RotateR(grandfather);cur->_color = BLACK;grandfather->_color = RED;}//调整完之后,就不需要继续改变了break;}}else   //grandfather->_right == parent{Node* uncle = grandfather->_left;//u为红if (uncle && uncle->_color == RED){parent->_color = uncle->_color = BLACK;grandfather->_color = RED;//继续向上调整cur = grandfather;parent = cur->_parent;}else //u不存在 或 存在且为黑{if (cur == parent->_right){//g//   p//      cRotateL(grandfather);parent->_color = BLACK;grandfather->_color = RED;}else{//g//   p//c	RotateR(parent);RotateL(grandfather);cur->_color = BLACK;grandfather->_color = RED;}//调整完之后,就不需要继续改变了break;}}}//根节点的颜色改成黑色_root->_color = BLACK;return true;}//判断该树是不是红黑树bool IsBalance(){return IsBalance(_root);}//计算红黑树的高度int Height(){return Height(_root);}private: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 CheckColor(Node* root, int blacknum, int benchmark){if (root == nullptr){if (blacknum != benchmark){return false;}return true;}//计算每条路径的黑色节点if (root->_color == BLACK){++blacknum;}if (root->_color == RED && root->_parent && root->_parent->_color == RED){cout << root->_kv.first << "出现连续红色节点" << endl;return false;}return CheckColor(root->_left, blacknum, benchmark)&& CheckColor(root->_right, blacknum, benchmark);}bool IsBalance(Node* root){if (root == nullptr){return true;}if (root->_color != BLACK){return false;}//基准值 -->  用于比较别的路径黑色节点个数int benchmark = 0;Node* cur = _root;while (cur){if (cur->_color == BLACK)benchmark++;cur = cur->_left;}return CheckColor(root, 0, benchmark);}//旋转//都是二叉树的旋转,所以和AVLTree的旋转一样,只不过这里没有平衡因子void RotateR(Node* parent){_rotateCount++;Node* cur = parent->_left;Node* curright = cur->_right;parent->_left = curright;if (curright)curright->_parent = parent;Node* ppnode = parent->_parent;cur->_right = parent;parent->_parent = cur;if (ppnode == nullptr){_root = cur;cur->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}}void RotateL(Node* parent){_rotateCount++;Node* cur = parent->_right;Node* curleft = cur->_left;//重新链接parent->_right = curleft;if (curleft)curleft->_parent = parent;cur->_left = parent;//提前保存parent->_parent,可能是根节点,也可能是子树的根节点Node* ppnode = parent->_parent;parent->_parent = cur;if (ppnode == nullptr){_root = cur;cur->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}}private:Node* _root = nullptr;
};

三、总结

红黑树和AVL树都是高效的平衡二叉树,增删查改的效率都是O(logN),红黑树不追求绝对平衡,只需要保证最长路径不超过最短路径的两倍,相对而言降低了旋转次数。所以红黑树更优一些

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

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

相关文章

【机器学习13】生成对抗网络

1 GANs的基本思想和训练过程 生成器用于合成“假”样本&#xff0c; 判别器用于判断输入的样本是真实的还是合成的。 生成器从先验分布中采得随机信号&#xff0c;经过神经网络的变换&#xff0c; 得到模拟样本&#xff1b; 判别器既接收来自生成器的模拟样本&#xff0c; 也接…

SPASS-曲线估计

基本概念 曲线估计&#xff08;曲线拟合、曲线回归&#xff09;则是研究两变量间非线性关系的一种方法&#xff0c;选定一种用方程表达的曲线&#xff0c;使得实际数据与理论数据之间的差异尽可能地小。如果曲线选择得好&#xff0c;那么可以揭示因变量与自变量的内在关系&…

【最新Tomcat】IntelliJ IDEA通用配置Tomcat教程(超详细)

前言 IntelliJ IDEA是一个强大的集成开发环境&#xff0c;能够大大简化Java应用程序的开发和部署过程。而Tomcat作为一个流行的Java Web服务器&#xff0c;其与IntelliJ IDEA的整合能够提供便捷的开发环境&#xff0c;让开发人员更专注于代码的创作与优化。 在配置IntelliJ IDE…

【心得】基于flask的SSTI个人笔记

目录 计算PIN码 例题1 SSTI的引用链 例题2 SSTI利用条件&#xff1a; 渲染字符串可控&#xff0c;也就说模板的内容可控 我们通过模板 语法 {{ xxx }}相当于变相的执行了服务器上的python代码 利用render_template_string函数参数可控&#xff0c;或者部分可控 render_…

日志维护库:loguru

在复杂的项目中&#xff0c;了解程序的运行状态变得至关重要。在这个过程中&#xff0c;日志记录&#xff08;logging&#xff09;成为我们追踪、调试和了解代码执行的不可或缺的工具。在python语言中常用logging日志库&#xff0c;但是logging日志库使用相对繁琐&#xff0c;在…

创新案例|云服务平台HashiCorp是如何构建开源社区实现B2B增长飞轮

社区文化是HashiCorp企业文化的重要组成部分。虽然众多公司声称自己是社区驱动&#xff0c;但实际付诸行动的很少。与众不同的是&#xff0c;HashiCorp从一开始就将社区视为战略方针的核心&#xff0c;这也影响和塑造了公司今天的发展方向。社区不仅是执行策略之一&#xff0c;…

Java多线程(3)

Java多线程(3) 深入剖析Java线程的生命周期&#xff0c;探秘JVM的线程状态&#xff01; 线程的生命周期 Java 线程的生命周期主要包括五个阶段&#xff1a;新建、就绪、运行、阻塞和销毁。 **新建&#xff08;New&#xff09;&#xff1a;**线程对象通过 new 关键字创建&…

SPASS-聚类和判别分析

聚类与判别分析概述 基本概念 聚类分析 聚类分析的基本思想是找出一些能够度量样本或指标之间相似程度的统计量,以这些统计量为划分类型的依据,把一些相似程度较大的样本(或指标)聚合为一类,把另外一些彼此之间相似程度较大的样本又聚合为一类。根据分类对象的不同,聚类…

Jenkins测完通知到人很麻烦?一个设置配置钉钉消息提醒!

Jenkins 作为最流行的开源持续集成平台&#xff0c;其强大的拓展功能一直备受测试人员及开发人员的青睐。大家都知道我们可以在 Jenkins 中安装 Email 插件支持构建之后通过邮件将结果及时通知到相关人员。但其实 Jenkins 还可以支持钉钉消息通知&#xff0c;其主要通过 DingTa…

高效管理文件:如何通过文件数量归类提高工作效率

在日常生活和工作中&#xff0c;需要处理大量的文件和资料。然而&#xff0c;如果这些文件没有得到妥善的管理&#xff0c;就会使得我们花费大量的时间和精力去寻找和整理它们。对于大量文件&#xff0c;按照数量归类可以使得文件管理更加有序和规范。根据文件的数量建立相应的…

算法设计与分析复习--贪心(二)

文章目录 上一篇哈夫曼编码单源最短路最小生成树Kruskal算法Prim算法 多机调度问题下一篇 上一篇 算法设计与分析复习–贪心&#xff08;一&#xff09; 哈夫曼编码 产生这种前缀码的方式称为哈夫曼树 哈夫曼树相关习题AcWing 148. 合并果子 #include <iostream> #inc…

三层交换机实现不同VLAN间通讯

默认时&#xff0c;同一个VLAN中的主机才能彼此通信&#xff0c;那么交换机上的VLAN用户之间如何通信&#xff1f; 要实现VLAN之间用户的通信&#xff0c;就必须借助路由器或三层交换机来完成。 下面以三层交换机为例子说明&#xff1a; 注意&#xff1a; 1.交换机与三层交换…