红黑树介绍与模拟实现(insert+颜色调整精美图示超详解哦)

红黑树

  • 引言
  • 红黑树的介绍
  • 实现
    • 结点类
    • insert
      • 搜索插入位置
      • 插入
      • 调整
        • 当parent为gparent的左子结点
        • 当parent为gparent的右子结点
  • 参考源码
  • 测试红黑树是否合格
  • 总结

引言

在上一篇文章中我们认识了高度平衡的平衡二叉树AVL树:戳我看AVL树详解哦
(关于旋转调整的部分,在AVL树的时候已经详细介绍过了,如果大家对旋转调平衡的部分有疑惑的话,请移步至AVL树的详解)

由于AVL树的高度平衡,其平均搜索时间复杂度几乎可以达到严格的O(logN)。同样也因为平衡的程度很高,在维护平衡上时间的花费对于搜索上时间的提升是得不偿失的。
大多数情况下,我们并不需要很高的平衡程度,只需要达成一种接近平衡的状态,搜索的平均时间复杂度基本达到O(logN)即可。 红黑树就是这样的一种结构,它的最高子树的高度小于等于最低子树的二倍。通过减少调平衡时的时间成本来提高效率

红黑树的介绍

红黑树是平衡二叉树的一种,他在满足二叉搜索树特性的基础上,给每个结点增加了一个颜色属性,包括RedBlack;并要求从根节点到一个叶子结点形成的任意一条路径中,通过对结点颜色的限制规则,没有任何一条路径回比其他的路径长一倍
在这里插入图片描述
(红黑圣诞树)

对于红黑树结点颜色的限制规则如下:

  • 每个结点的颜色只有红色或黑色两种
  • 根结点的颜色一定是黑色的
  • 如果某一个结点的颜色是红色的,它的两个孩子结点的颜色一定是黑色(即不存在两个连续的红色结点);
  • 对于任一结点,到叶子结点的任一路径上,包含的黑色结点的数量一定相等
  • 叶子结点一定是黑色的(这里的叶子结点直最后的nullptr

当满足上面的所有规则时,根节点到叶子结点的任一路径就都不可能比其他路径长一倍。由于不存在连续的红色结点,所以当黑色结点的数量 n 一定时,最长路径的长度为2 * n,最短路径的长度为 n ,所以不可能相差一倍以上。这样就达成了一种相对平衡的状态,并不需要经常去旋转调平了。

实现

红黑树的实现,在之前的二叉搜索树上增加了结点的颜色,以及对于结点的颜色调整的部分;
在本篇文章中依旧实现 K-V的模式 的树, 并且以非递归实现insert
为防止命名冲突,将实现放在我们的命名空间qqq中:

结点的颜色我们使用 枚举常量enum Color 来表示;

RBTree是一个类模板,有两个模板参数,即KV,表示其中存储的索引类型与值类型;
成员变量类型为Node*(由结点类RBTreeNode重命名),表示根结点的指针:

 //基本的代码结构
namespace qqq
{enum Color //枚举常量表示颜色{RED,BLUCK};template<class K, class V>struct RBTreeNode  //结点类{};template<class K, class V>class RBTree  //红黑树{typedef RBTreeNode<K, V> Node;public:bool insert(const pair<K, V>& kv){}protected:Node* _root = nullptr;};
}

结点类

首先,对红黑树的结点进行实现:

RBTreeNode是也一个类模板, 两个模板参数同样为KV,表示索引与值的类型;
在结点中储存数据的结构为pair,其中firstK类型,secondV类型;

成员变量包括结点中的数据 _kv
指向父亲结点的指针 _parent
指向左右孩子结点的指针 _left_right
表示结点颜色的枚举常量 _col

template<class K, class V>
struct RBTreeNode  //结点类
{pair<K, V> _kv;RBTreeNode<K, V>* _parent;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;Color _col;
};

结点类的构造函数
我们需要实现一个默认构造函数:
参数类型为const pair<K, V>,缺省值为一个pair<K, V>的匿名对象;
对于父子结点的指针,在初始化列表中初始化为nullptr即可;
对于结点的颜色,在初始化列表中初始化为RED(要保证每条路上黑色结点的个数相等,就必须初始化为红色):

	RBTreeNode(const pair<K, V> kv = pair<K, V>()): _kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr), _col(RED){}

insert

红黑树的insert分为两个部分,即搜索并插入以及调整使其满足红黑树的性质:

搜索插入位置

  • 与二叉搜索树类似,搜索并插入时,首先用要插入的pair对象创建一个新结点newnode
  • 与此同时,使用结点指针 cur来记录当前位置parent来记录cur的父结点,便于后面插入;
  • 然后 while循环向下查找 插入的位置:当newnode小于cur的元素时,向左查找,当newnode大于cur的元素时,向右查找,相等时即该元素已经存在,返回false
  • curnullptr时,表示找到了插入的位置,循环终止:
// 部分代码:搜索插入位置 //
Node* newnode = new Node(kv);
Node* parent = nullptr;
Node* cur = _root;while (cur != nullptr) //搜索
{if (newnode->_kv.first > cur->_kv.first){parent = cur;cur = parent->_right;}else if (newnode->_kv.first < cur->_kv.first){parent = cur;cur = parent->_left;}else  //相等即插入失败{return false;}
}

插入

  • newnode插入红黑树即将newnodeparent链接,这时就需要判断parent是否为空
  • parent为空时,即cur就是根节点_root,即newnode是这棵红黑树中的第一个结点,将 其赋值给_root 即可;
  • parent不为空时, 还需要判断cur位于parent的左边还是右边,然后再插入:

在这里插入图片描述

//  局部代码:插入newnode //
if (parent == nullptr) //插入
{_root = newnode;
}
else if (newnode->_kv.first < parent->_kv.first)
{parent->_left = newnode;newnode->_parent = parent;cur = newnode;
}
else
{parent->_right = newnode;newnode->_parent = parent;cur = newnode;
}

调整

在完成插入后,首先需要判断的是parent指针的状态:

  • parent为空时,表明当前结点为根结点,将其颜色改为BLACK即可调整完毕;
  • parent指向结点的颜色为BLACK 时,如果是在刚插入时,新插入的结点颜色为RED,不会影响该路径的黑色节点个数,所以不再需要调整。如果是在向上调整的过程中parent指向的结点为BLACK,也意味着整棵树调整结束了(这一点在后面的调整部分会详细介绍);
  • 除了上面不用调整的两种情况外,其余的情况,即 parent指向的结点存在且为RED 的情况就需要进行调整了。调整是自下而上的,循环向上调整,直到 parent为空或parent指向的结点为BLACK时循环结束,调整完成
// 部分代码:自下而上调整的循环框架 //
while (parent != nullptr && parent->_col != BLUCK) 
{}

开始调整时首先对parentgparentcur的祖父结点)的左子结点还是右子结点做一分类讨论(当parent不为黑时,由于根结点必须为黑,所以parent不是根结点,所以gparent一定存在):

当parent为gparent的左子结点

parentgparent的左子结点时,我们首先要考察cur叔叔结点的情况:

  • cur的叔叔结点为RED 时,不需要进行旋转调整,只需要将gparent指向结点设置为RED,将parentcur的叔叔结点全部设置为BLACK即可:
    由于在调整完颜色后,gparent指向结点颜色就为RED了,这时如果gparent父结点的颜色正好为红,就出现了连续的两个红色结点。所以需要cur向上移动两个结点,再对其parent指向的结点继续进行判断:
    在这里插入图片描述

  • cur的叔叔结点为BLACK或不存在时,就需要进行旋转调整了:
    旋转的逻辑与之前AVL树类似,当cur位于parent的左边时,即左左——单次右旋
    在这里插入图片描述
    cur位于parent的右边时,即左右——左右双旋
    在这里插入图片描述
    在这里插入图片描述
    旋转调整后将gparent指向结点设置为RED,将子树顶部的结点设置为BLACK 即可(parent指向的结点或cur指向的结点):
    在这里插入图片描述

经过旋转后的红黑树,子树顶部的颜色一定为黑色,再向上也就不会存在两个连续的红色结点的问题了,所以旋转之后直接终止循环即可。

需要注意的是叔叔是黑色结点的情况一定是出现在调整过程中发生的,当叔叔结点为黑色时,cur下的路径中一定存在着与gparent右子树中黑色结点相同个数的黑色结点,parent下的路径中同样也存在着相同数目的黑色结点,这样在旋转调平衡后,这棵子树中的所有路径中的黑色结点数目与之前是不变的

//部分代码:当parent为gparent左子结点的情况
if (parent == gparent->_left)   
{if (gparent->_right == nullptr || gparent->_right->_col == BLUCK)  //1.叔叔结点不存在或为黑,需要旋转并调色{if (cur == parent->_left)//左左->单次右旋{RotateR(gparent);parent->_col = BLUCK;gparent->_col = RED;}else//左右->左旋+右旋{RotateL(parent);RotateR(gparent);cur->_col = BLUCK;gparent->_col = RED;}break; //通过旋转调整后,该子树的根结点一定是黑,所以可以直接结束循环}else if (gparent->_right->_col == RED)  //2.叔叔结点为红,通过调色即可实现红黑树{//调色parent->_col = BLUCK;gparent->_right->_col = BLUCK;gparent->_col = RED;//继续向上cur = gparent;parent = cur->_parent;}else{assert(0);}
}
当parent为gparent的右子结点

parentgparent的右子结点时,与上面的情况一致,只是左右对调了,所以这里只给出图示与代码(如果在这种情况下遇到了问题,希望你在上面的情况中能够找到答案):

  • 叔叔结点为RED,仅调整颜色
    在这里插入图片描述

  • 叔叔结点为BLACK或不存在,左单旋或右左双旋:
    左单旋
    在这里插入图片描述
    右左双旋
    在这里插入图片描述在这里插入图片描述
    旋转后调整颜色
    在这里插入图片描述

旋转后子树顶部的结点一定为BLACK,所以直接break即可。

//部分代码:当parent为gparent右子结点的情况 //
else //parent == gparent->_right
{if (gparent->_left == nullptr || gparent->_left->_col == BLUCK)  //1.叔叔结点不存在或为黑,需要旋转并调色{if (cur == parent->_right)//右右->单次左旋{RotateL(gparent);parent->_col = BLUCK;gparent->_col = RED;}else//右左->右旋+左旋{RotateR(parent);RotateL(gparent);cur->_col = BLUCK;gparent->_col = RED;}break; //通过旋转调整后,该子树的根结点一定是黑,所以可以直接结束循环}else if (gparent->_left->_col == RED)  //2.叔叔结点为红,通过调色即可实现红黑树{//调色parent->_col = BLUCK;gparent->_left->_col = BLUCK;gparent->_col = RED;//继续向上cur = gparent;parent = cur->_parent;}else{assert(0);}
}

while循环调整结束之后,再将根结点_root的颜色改为BLACK,统一做处理(insert的整体代码在这里就不做展示了,大家跳转至参考源码部分查看即可)。

参考源码

namespace qqq
{enum Color //枚举常量表示颜色{RED,BLUCK};template<class K, class V>struct RBTreeNode  //结点类{pair<K, V> _kv;RBTreeNode<K, V>* _parent;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;Color _col;RBTreeNode(const pair<K, V> kv = pair<K, V>()): _kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr), _col(RED){}};template<class K, class V>class RBTree  //红黑树{typedef RBTreeNode<K, V> Node;public:bool insert(const pair<K, V>& kv){ //先插入Node* newnode = new Node(kv);Node* parent = nullptr;Node* cur = _root;while (cur != nullptr) //搜索{if (newnode->_kv.first > cur->_kv.first){parent = cur;cur = parent->_right;}else if (newnode->_kv.first < cur->_kv.first){parent = cur;cur = parent->_left;}else  //相等即插入失败{return false;}}if (parent == nullptr) //插入{_root = newnode;}else if (newnode->_kv.first < parent->_kv.first){parent->_left = newnode;newnode->_parent = parent;cur = newnode;}else{parent->_right = newnode;newnode->_parent = parent;cur = newnode;}//调整颜色以及旋转使满足红黑树while (parent != nullptr && parent->_col != BLUCK) //当parent不为黑时,由于根结点必须为黑,所以parent不是根结点,所以gparent一定存在{Node* gparent = parent->_parent;if (parent == gparent->_left)   {if (gparent->_right == nullptr || gparent->_right->_col == BLUCK)  //1.叔叔结点不存在或为黑,需要旋转并调色{if (cur == parent->_left)//左左->单次右旋{RotateR(gparent);parent->_col = BLUCK;gparent->_col = RED;}else//左右->左旋+右旋{RotateL(parent);RotateR(gparent);cur->_col = BLUCK;gparent->_col = RED;}break; //通过旋转调整后,该子树的根结点一定是黑,所以可以直接结束循环}else if (gparent->_right->_col == RED)  //2.叔叔结点为红,通过调色即可实现红黑树{//调色parent->_col = BLUCK;gparent->_right->_col = BLUCK;gparent->_col = RED;//继续向上cur = gparent;parent = cur->_parent;}else{assert(0);}}else{if (gparent->_left == nullptr || gparent->_left->_col == BLUCK)  //1.叔叔结点不存在或为黑,需要旋转并调色{if (cur == parent->_right)//右右->单次左旋{RotateL(gparent);parent->_col = BLUCK;gparent->_col = RED;}else//右左->右旋+左旋{RotateR(parent);RotateL(gparent);cur->_col = BLUCK;gparent->_col = RED;}break; //通过旋转调整后,该子树的根结点一定是黑,所以可以直接结束循环}else if (gparent->_left->_col == RED)  //2.叔叔结点为红,通过调色即可实现红黑树{//调色parent->_col = BLUCK;gparent->_left->_col = BLUCK;gparent->_col = RED;//继续向上cur = gparent;parent = cur->_parent;}else{assert(0);}}}_root->_col = BLUCK;return true;}void RotateL(Node* parent){Node* gparent = parent->_parent;Node* cur = parent->_right;Node* curleft = cur->_left;parent->_right = curleft;if (curleft != nullptr){curleft->_parent = parent;}cur->_left = parent;parent->_parent = cur;cur->_parent = gparent;if (gparent == nullptr){_root = cur;}else{if (cur->_kv.first < gparent->_kv.first){gparent->_left = cur;}else{gparent->_right = cur;}}}void RotateR(Node* parent){Node* gparent = parent->_parent;Node* cur = parent->_left;Node* curright = cur->_right;parent->_left = curright;if (curright != nullptr){curright->_parent = parent;}cur->_right = parent;parent->_parent = cur;cur->_parent = gparent;if (gparent == nullptr){_root = cur;}else{if (cur->_kv.first < gparent->_kv.first){gparent->_left = cur;}else{gparent->_right = cur;}}}bool isRBTree(){return isRBTree(_root);}protected:Node* _root = nullptr;bool checkColor(Node* root, int countBluck, int& baseBluck) //计算黑结点数量与红结点连续是否满足条件{if (root == nullptr){if (baseBluck == countBluck){return true;}return false;}if (root->_col == RED && root->_parent != nullptr && root->_parent->_col == RED){return false;}if (root->_col == BLUCK){countBluck++;}return checkColor(root->_left, countBluck, baseBluck) && checkColor(root->_right, countBluck, baseBluck);}bool isRBTree(Node* root){if (root == nullptr)return true;if (root->_col == RED)return false;//先计算一条路径中的黑色结点数量int baseBluck = 0;Node* cur = root;while (cur != nullptr){if (cur->_col == BLUCK){++baseBluck;}cur = cur->_right;}return checkColor(root, 0, baseBluck);}};
}

测试红黑树是否合格

在写完红黑树的insert之后,我们可以再编写一个测试模块来测试一棵树是否满足红黑树的特性

我们其实只需要判断两点即可:

  • 任一路径上的黑色结点个数是否相等
  • 是否存在连续的两个红色结点

我们使用递归的方式来判断:

判断任一路径上的黑色结点个数是否相等时,必须要先计算某条路径上的黑色结点个数,可以通过 while循环计算最右侧一条路径上的黑色结点个数。将cur_root开始一直向右子节点遍历即可,当cur为空时即该路径结束,终止循环。

然后使用checkColor函数(参数为当前结点指针Node*,计算中的当前路径黑色结点个数int以及先前计算的最外层黑色结点个数int )递归计算每条路径上的黑色结点个数,顺便判断是否存在连续的红色结点。
在递归过程中:
_root为空时,即当前路径已经结束,判断countBluckbaseBlack的值是否相等,若相等返回true,否则返回false
若当前结点为红色,则判断其父结点是否为红色,是就返回false
若当前结点为黑色,countBluck加1,并继续向左右子结点递归,返回左子结点的结果&&右子结点的结果:
在这里插入图片描述

测试红黑树是否合格的代码这里就不赘述了,大家可以在参考源码部分查找。这里来展示一下测试结果

namespace qqq
{void testfunc(){const int N = 10000000;vector<int> v;v.reserve(N);srand(time(0));for (size_t i = 0; i < N; i++){v.push_back(i);}RBTree<int, int> rbt;for (auto e : v){rbt.insert(make_pair(e, e));cout << "insert:" << e << "->" << rbt.isRBTree() << endl;}cout << rbt.isRBTree() << endl;}
}int main()
{qqq::testfunc();return 0;
}

在这里插入图片描述
因为这段测试代码中,存在大量I/O,所以运行速度很慢,大家可以将cout注释掉,只打印最后的结果。

总结

到此,关于红黑树的知识就介绍完了
在接下来的文章中,将会对红黑树进行封装,即mapset,尽情期待哦

如果大家认为我对某一部分没有介绍清楚或者某一部分出了问题,欢迎大家在评论区提出

如果本文对你有帮助,希望一键三连哦

希望与大家共同进步哦

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

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

相关文章

Java 7、Java 8常用新特性

目录 Java 8 常用新特性1、Lambda 表达式2、方法引用2.1 静态方法引用2.2 特定对象的实例方法引用2.3 特定类型的任意对象的实例方法引用2.4 构造器引用 3、接口中的默认方法4、函数式接口4.1 自定义函数式接口4.2 内置函数式接口 5、Date/Time API6、Optional 容器类型7、Stre…

(四) 序列化器类使用整理

从一、序列化器类中&#xff0c;或 视图集源码 中&#xff0c; 可以得知&#xff1a; 序列化器类可以接收一个instance &#xff0c;和一个data serializer_obj XxxxSerializer(instance,datarequest.data) &#xff08;更新时&#xff0c;instance相当于原…

云原生技术精选:探索腾讯云容器与函数计算的最佳实践

文章目录 写在前面《2023腾讯云容器和函数计算技术实践精选集》深度解读案例集特色&#xff1a;腾讯云的创新实践与技术突破精选案例分析——Stable Diffusion云原生部署的最佳实践精选集实用建议分享总结 写在前面 在数字化转型的浪潮下&#xff0c;云计算技术已成为企业运营…

Kafka入门到实战-第五弹

Kafka入门到实战 Kafka常见操作官网地址Kafka概述Kafka的基础操作更新计划 Kafka常见操作 官网地址 声明: 由于操作系统, 版本更新等原因, 文章所列内容不一定100%复现, 还要以官方信息为准 https://kafka.apache.org/Kafka概述 Apache Kafka 是一个开源的分布式事件流平台&…

基于springboot+vue实现的酒店客房管理系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】&#xff1a;Java 【框架】&#xff1a;spring…

昇腾训练执行与推理部署系列 入门: 1.开启异腾AI之旅

一、1认识CANN 1、昇腾AI基础软硬件平台介绍2、CANN逻辑架构介绍 1、昇腾AI基础软硬件平台介绍 2、CANN逻辑架构介绍

普联一面4.2面试记录

普联一面4.2面试记录 文章目录 普联一面4.2面试记录1.jdk和jre的区别2.java的容器有哪些3.list set map的区别4.get和post的区别5.哪个更安全6.java哪些集合类是线程安全的7.创建线程有哪几种方式8.线程的状态有哪几种9.线程的run和start的区别10.什么是java序列化11.redis的优…

商品购买过程中,库存的抵扣过程是怎样的?如何防止超卖?

在商品购买的过程中&#xff0c;库存的抵扣过程&#xff0c;一般操作如下&#xff1a; 1、select根据商品id查询商品的库存。 2、根据下单的数量&#xff0c;计算库存是否足够&#xff0c;如果存库不足则抛出库存不足的异常&#xff0c;如果库存足够&#xff0c;则减去扣除的…

mysql+keepalive+lvs搭建的数据库集群实验

前提条件&#xff1a;准备5台计算机&#xff0c;且网络互通 1、客户端 yum groups -y install mariadb-client ip 192.168.0.5 2、lvs1 yum-y install ipvsadm keepalived ip 192.168.0.1 keepalivedvip 192.168.0.215 /etc/hosts 解析192.168.0.1 主机名 3、lvs2 yum-y i…

前视声呐目标识别定位(五)-代码解析之修改声呐参数

前视声呐目标识别定位&#xff08;一&#xff09;-基础知识 前视声呐目标识别定位&#xff08;二&#xff09;-目标识别定位模块 前视声呐目标识别定位&#xff08;三&#xff09;-部署至机器人 前视声呐目标识别定位&#xff08;四&#xff09;-代码解析之启动识别模块 …

DHT11温湿度传感器使用视频教程分享

下载地址&#xff1a; 温湿度计(STCDHT11): https://url83.ctfile.com/d/45573183-60623983-9b7f6c?p7526 (访问密码: 7526)

Java常用类和基础API

文章目录 1. 字符串相关类之不可变字符序列&#xff1a;String1.1 String的特性1.2 String的内存结构1.2.1 概述1.2.2 练习类型1&#xff1a;拼接1.2.3 练习类型2&#xff1a;new1.2.4 练习类型3&#xff1a;intern() 1.3 String的常用API-11.3.1 构造器1.3.2 字符串对象的比较…

解决酷狗官网无法播放音乐问题

播放歌曲页面无法播放歌曲&#xff0c;有CORS错误&#xff0c;困扰我很久了。 谷歌浏览器优雅的关闭cors跨域_浏览器关闭跨域-CSDN博客 "C:\Program Files\CatsxpSoftware\Catsxp-Browser\Application\catsxp.exe" --disable-web-security --user-data-dirE:\catsxp…

【学习笔记】java项目—苍穹外卖day10

文章目录 苍穹外卖-day10课程内容1. Spring Task1.1 介绍1.2 cron表达式1.3 入门案例1.3.1 Spring Task使用步骤1.3.2 代码开发1.3.3 功能测试 2.订单状态定时处理2.1 需求分析2.2 代码开发2.3 功能测试 3. WebSocket3.1 介绍3.2 入门案例3.2.1 案例分析3.2.2 代码开发3.2.3 功…

Fusion360修改嘉立创EDA专业版生成的3D外壳文件

需要第三方软件的原因 嘉立创EDA专业版生成电路板的3D外壳文件是比较快捷的&#xff0c;但如果侧面精密开孔或者添加其它非常规的元素还是有些局限。嘉立创EDA专业版可以把3D外壳文件导出&#xff0c;这就大大方便了第三方软件的修改。 本文是利用Fusion360修改3D外壳文件&…

Waifu2x:使用深度卷积神经网络的动漫风格艺术的图像超分辨率

Github网址&#xff1a;nagadomi/waifu2x&#xff1a;动漫风格艺术的图像超分辨率 (github.com) 该项目主要讲述的是如何利用预训练的深度学习模型来达到无损扩大收缩和去噪&#xff0c;对于一般训练图像的小伙伴应该很清晰图像经常要通过resize操作固定大小&#xff0c;然后c…

某华为hr擅自给我报名机

某华为hr擅自报名机考 上周&#xff0c;牛客网一位同学爆料&#xff1a; ❝ 今年一月份&#xff0c;华为一位 HR 通过某招聘网站联系到他&#xff0c;然后通过该 HR 加上了业务工程师的微信。随后&#xff0c;该工程师在「经过他本人同意」的情况下&#xff0c;在系统录入了他的…

CSGO比赛赛事大科普,Major并不是一个赛事!

关于CSGO比赛&#xff0c;有很多人都听过许多相关名词&#xff1a;Major、Minor、IEM、EPL、ESL ONE、Dreamhack、ESEA、Blast、EPICENTER等等&#xff0c;但大家有没有想过这些名词所代表的含义呢&#xff1f; Major、Minor严格意义上说&#xff0c;Major、Minor本身并不是赛事…

深入浅出 -- 系统架构之分布式架构

​​​​​​分布式架构&#xff1a; 根据业务功能对系统做拆分&#xff0c;每个业务功能模块作为独立项目开发&#xff0c;称为一个服务。 当垂直应用越来越多时&#xff0c;应用之间的交互不可避免&#xff0c;可将共用的基础服务或核心模块抽取出来作为独立服务&#xff0c…

接口练习题目

练习一 1、声明接口Eatable&#xff0c;包含抽象方法public abstract void eat(); 2、声明实现类中国人Chinese&#xff0c;重写抽象方法&#xff0c;打印用筷子吃饭 3、声明实现类美国人American&#xff0c;重写抽象方法&#xff0c;打印用刀叉吃饭 4、声明实现类印度人Indi…