AVL树

一.AVL树的概念

AVL树是一颗特殊的二叉搜索树。二叉搜索树在有些极端情况下可能会出现单支的情况,这会影响其插入查找的效率。而AVL树是一个高度平衡的二叉搜索树,它要求任何的左右子树的高低差都小于等于1。它可以通过去控制左右子树的高度差来控制二叉树的平衡,让二叉树能一直保持近似完全二叉树的形态,加快了插入和查找的效率O(logN)。

AVL树的实现里,我们引入了一个平衡因子(balance factor)的概念 ,每个节点都有一个平衡因子,所有的平衡因子的值都是右子树高度-左子树的高度。所以再AVL树中,所有的平衡因子都在-1~1之间,如果超过了这个范围,说明这颗AVL树已经不平衡了,我们需要通过旋转来使其重新达到平衡。

二.AVL树的结构

 AVL树也是key/value的场景,而且我们在树的节点中还加入了执行父亲节点的指针以及平衡因子,借助三叉链和平衡因子来控制AVL树的平衡。

template <typename K, typename V>
struct AVlTreeNode
{pair<K, V> _kv;AVlTreeNode<K, V>* _left;AVlTreeNode<K, V>* _right;AVlTreeNode<K, V>* _parent;int _bf = 0;AVlTreeNode(const pair<K,V>& kv): _kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr){}
};template <typename K, typename V>
class AVLtree
{typedef AVlTreeNode<K, V> Node;private:Node* _root = nullptr;
};

三.AVL树的插入

1.插入的步骤

1、首先按照二叉搜索树的插入逻辑,左右比较,找到要插入的位置,然后插入。

2、插入了一个节点之后,可能会影响这颗树的高度,也就是会影响这个节点的祖先节点的平衡因子,所以我们要沿着这个路径:新插入的节点->根节点,开始更新平衡因子,有可能更新到某个祖先就停止了,最坏的情况会更新到根节点。

3、更新过程中没有遇到问题则更新结束,插入动作完成。

4、更新平衡因子过程中如果遇到不平衡的情况:平衡因子绝对值大于1,此时就需要进行旋转,来使这棵树的高度降低,重新达到平衡,插入动作完成。

2.平衡因子的更新

2.1平衡因子的更新原则

  • 平衡因子 = 右子树高度 - 左子树高度
  • 新插入的节点的平衡因为 == 0,因为插入的都是叶子节点
  • 新增节点,会引起树的高度变化,当在parent的左子树插入时,parent的平衡因子--,右子树插入平衡因子++
  • parent所在的子树高度是否变化决定了是否继续向上更新平衡因子

 分析:就如上面的插入实例来说,在14左边插入13节点,所以14的平衡因子--,变成-1,而对于14这颗子树来说,它的高度本来是1,现在变成了2,高度发生了变化,所以要继续向上更新平衡因子。

2.2更新平衡因子的结束条件

根据AVL树的要求,平衡因子其实只有三种取值,0、-1/1、2/-2。

当更新后平衡因子为0,停止更新。

平衡因子变成0,说明该节点原本的平衡因为为-1/1,即该节点原本只有一个孩子,插入了一个节点后,使左右平衡了,此时高度没有发生变化,所以更新停止。

当更新后平衡因子为-1/1,继续向祖先进行更新。

平衡因子变成-1/1,说明之前的平衡因子为0(不可能为2/-2,如果是2/-2的话,说明这棵树已经不平衡了,需要进行旋转),即这个节点的左右子树高度相同,但是插入一个之后,使其高度发生了变化,所以要继续向上更新。 

当更新后平衡因子为2/-2,停止更新,进行旋转操作

平衡因子变为2/-2,此时已经不满足AVL树的要求——左右子树的高度差小于等于1.此时也要停止更新,进行旋转操作,旋转操作可以在保证AVL树的规则下,平衡该树使其高度降低,重新满足AVL树的要求。

3.节点的插入以及平衡因子的更新 

当更新过程中,parent走到了nullptr,即parent已经使root本身了,root->_parent == nullptr,此时已经没有节点可以更新了,所以也要停止更新。

bbool Insert(const pair<K,V>& kv)
{//树为空if (_root == nullptr){_root = new Node(kv);return true;}Node* cur = _root;Node* parent = nullptr;while (cur){if (kv.first > cur->_kv.first){parent = cur;cur = cur->_right;}else if (kv.first < cur->_kv.first){parent = cur;cur = cur->_left;}else{return false;}}//创建新节点,连接到树上,更新父亲节点的平衡因子cur = new Node(kv);if (parent->_kv.first > cur->_kv.first){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;//更新平衡因子while (parent){if (parent->_left == cur){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{//一般情况不会进入这个else,如果进入则说明一定平衡因子_bf出错了//这里直接断言报错,可以让我们将问题定位到_bfassert(false);}}return true;
}

四.旋转

1.旋转的原则

  1. 保持搜索树的规则
  2. 让旋转的树从不平衡变平衡,其次降低旋转树的高度

旋转分为两大类——单旋和双旋,而单旋有左单旋右单旋,双旋有左右双旋右左双旋。

2.右单旋

  1. 下面是以10为根节点的一棵抽象二叉树, 它有三棵抽象的子树a、b、c(都是AVL树),且高度都是h。但是因为5节点的存在,左子树的高度为h+1,右子树高度为h,所以10的平衡因子为-1.
  2. 当我们插入往a子树插入一个节点时,此时就要对平衡因子进行更新,更新到节点10时,它的平衡因子变成了-2,此时就要进行旋转。又因为对于10这个节点来说,它的左边高于右边;对于5节点来说,也是左边高于右边,所以要进行右单旋。
  3. 因为这是一个二叉搜索树,所以b子树上的所有结点的值都大于5小于10,所以我们将b变成10的左子树,接着将10变为5的右子树,最后让5成为新的根。此时这棵树的左右子树就重新恢复了平衡,且依旧满足二叉搜索树的规则。
  4. 最后一步就是重新更新这棵树里面的平衡因子,我们可以看到10的左右子树最后高度都是h,所以平衡因子为0,5的左右子树高度都为h+1,所以平衡因子也是0.

 上面我们是将所有情况都抽象出来进行分析的,下面我们来分析一下一些具体情况:

当a/b/c的高度都是0时,此时高度不平衡,其满足右单旋的规则,所以我们将b->nullptr作为10的左子树,然后将10作为5的右子树,最后让5成为新的根。此时就完成了右单旋,平衡因子也更新成为0.

当a/b/c的高度为1时,我们依旧采取之前的原则,将b作为10的左子树,10作为5的右子树,然后5作为新的根,5和10的平衡因子更新都更新成为0.

  • 需要注意的是,我们这里实现AVL树时使用的三叉链结构,所以当我们旋转时改变了左右子树的指向的同时也要改变_parent的指针指向,让其指向新的父亲。
  • 还有就是这棵树有可能就是整个树,即10就是整个树的根,但是也有可能只是一颗子树而已。如果只是一个子树的话,我们就不能仅仅让5为了新的根了,要让其指向parent的parent。

 3.1右单旋代码

我们前面说了满足右单旋是因为对于10来说,它的左子树高于右子树,对于4来说,它的左子树高于右子树。只有同时满足这两个要求,才可以进行右单旋。

将这句话转换为平衡因子也就是10的平衡因子为-2,5的平衡因子为-1时,进行右单旋。

//旋转
if (parent->_bf == -2 && cur->_bf == -1)
{RotateR(parent);
}
void RotateR(const Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;Node* pParent = parent->_parent;//为了避免10不是整棵树的根,所以要记录下parent的parentparent->_left = subLR;if (subLR){subLR->_parent = parent;}subL->_right = parent;parent->_parent = subL;if (parent == _root){subL = _root;subL->_parent = nullptr;}else//如果parent不是根,那么要判断subL连接到pParent的左子树还是右子树{if (pParent->_left == parent){pParent->_left = subL;}else{pParent->_right = subL;}subL->_parent = pParent;}//更新平衡因子parent->_bf = 0;subL->_bf = 0;
}

4.左单旋

左单旋和右单旋是非常相似的。

我们看下面这棵抽象的树,当我们在a这棵子树下面插入一个节点,导致这棵子树的高度变化,15的平衡因子就要变为-1,根据之前的更新规则,-1要继续向上更新。当我们更新到15这个节点时,发现平衡因为变为了2,此时就要进行旋转,那么进行那个旋转呢?还能进行右单旋嘛?

进行右单旋是因为对于整棵树左边是更高的,而对于下面这种情况,右边的子树高度更高,所以我们要进行左单旋,让右边的高度降低。

左单旋的操作与右单旋类似:我们将b作为10的右子树,10作为15的左子树,这样就可以达到我们的目的

4.1左单旋代码 

//左单旋条件
else if (parent->_bf == 2 && cur->_bf == 1)
{RotateL(parent);
}
void RotateL(const 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;subR->_parent = nullptr;}else{if (pParent->_left == parent){pParent->_left = subR;}else{pParent->_right = subR;}subR->_parent = pParent;}subR->_bf = 0;parent->_bf = 0;
}

3.左右双旋

我们上面两种情况都是纯粹的一边高。所以可以采用单旋的方式来使其恢复平衡。

 而当我们遇到下面这种情况的话,直接左单旋/右单旋是解决不了问题的:

当我们在5的右边插入一个节点,此时对于5这棵子树是右边高,对于10这棵子树是左边高,遇到这种情况,我们如果把他当作左边高,直接使用右单旋是达不到目的的。

对于上面这种情况,我们要采取双旋的方式来解决。

我们依旧采取将所有情况都抽象出来进行分析:

a/b/c如果不为空,那么也是AVL树,满足AVL树的规则。当我们将节点插入到b子树时,这就要采取双旋来解决问题:我们将b子树的左子树作为节点5的右子树,b子树的右子树作为10的左子树,然后b子树的根作为新的根,5和10分别作为新根的左右子树。

而上面的过程其实就是先对5那棵树进行一个左单旋,然后对10这棵树在进行一次右单旋,就完成了我们上面的过程。

但是插入节点在b子树的插入位置的不同,会影响旋转后平衡因子的更新不同。所以我们将b子树拆分开来,分析插入daob子树的不同位置,引起的不同的变化。

  • 场景一:当h>=1时,新增节点在e子树,高度由h-1变为h,8、5、10的平衡因子都要进行更新,10的平衡因子变为-2,此时要进行旋转,根据上面的旋转规则,5的左右子树高度相同,平衡因子为0,10的平衡因为为1,而8的平衡因子为0
  • 场景二:当h>=1时,新增节点在f子树,此时也要进行旋转,但是这种情况下,5的平衡因子变为-1,10的平衡因子变为0,8的平衡因子为0.
  • 场景三:当h == 0时,新增节点的左右子树都为空,旋转后5、10的左右子树都为空,然后新增节点就是新的根,左右孩子分别为5,10.

 3.1左右单旋代码

我们经过上面的分析,插入一个节点之后,对不同的两个节点来说,它们的高的子树位置不同,我们就要分别采用两次旋转来解决,如果下面的子树右边高,外面的子树左边高,此时就要先左旋后右旋来解决。

 调用左右双旋的前提

else if (parent->_bf == -2 && cur->_bf == 1)
{RotateLR(parent);
}
void RotateLR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);if (bf == -1){subL->_bf = 0;subLR->_bf = 0;parent->_bf = 1;}else if(bf == 1){subL->_bf = -1;subLR->_bf = 0;parent->_bf = 0;}else if (bf == 0){subL->_bf = 0;subLR->_bf = 0;parent->_bf = 0;}else{assert(false);}
}

4.右左双旋

右左双旋与左右双旋类似,先右旋再左旋。

右左双旋说明对于内层的树来说,左子树高,对于外层的树来说,右子树高。所以我们对内层的根先右旋,在对外层的根左旋。接下来只需要分析插入位置不同导致的平衡因子的更新不同即可。

4.1右左双旋代码 

else if (parent->_bf == 2 && cur->_bf == -1)
{RotateRL(parent);
}
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);}
}

旋转总结: 

我们可以直接根据parent与cur的平衡因子的值来判断需要用什么旋转:

首先,当更新平衡因子后,出现2/-2才需要进行旋转

如果这个parent节点与cur节点同号则单旋,同负右单旋,同正左单旋;异号双旋,parent为正,右左双旋,parent为负,左右双旋。

 五.AVL树的查找

AVL树的查找与二叉搜索树相同,比较查找:大于往右边找,小于往左边找。比较时只用key作为关键码来比较。

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

六.AVL树的测试

我们可以借助这些代码来测试你的AVL树是否正确,也可以测试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 = 1000000;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;
}

测试所需代码: 

//测试所需代码:
public:void InOrder(){_InOrder(_root);cout << endl;}int Height(){return _Height(_root);}int Size(){return _Size(_root);}bool IsBalanceTree(){return _IsBalanceTree(_root);}
private:void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);}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;}int _Size(Node* root){if (root == nullptr)return 0;return _Size(root->_left) + _Size(root->_right) + 1;}bool _IsBalanceTree(Node* root){// 空树也是AVL树if (nullptr == root)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){cout << root->_kv.first << "高度差异常" << endl;return false;}if (root->_bf != diff){cout << root->_kv.first << "平衡因子异常" << endl;return false;}// pRoot的左和右如果都是AVL树,则该树一定是AVL树return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);}

完~

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

鸿蒙开发-网络数据访问、应用本地数据保存

HTTP概述 HTTP&#xff0c;全称Hyper Text Transfer Protocol 超文本传输协议。 HTTP请求为短连接。客户端发起请求&#xff0c;服务器返回响应。本次连接即结束。 添加网络权限 在访问网络之前&#xff0c;需要在module.json5中给APP添加网络权限 "module": {&…

画 五边形 思路

1. 计算圆心 view 中心点 2.规定半径 R < view宽度 / 2 3.计算五边形五个顶点&#xff08;角度A 2π / 5&#xff09; 4. 五点相连 转载&#xff1a; Android自定义控件 芝麻信用分雷达图 - 简书

网络工程实验三:DHCP的配置

#实验仅供参考&#xff0c;勿直接粘贴复制&#xff0c;用以学习交流# #对于软件的使用&#xff0c;请移步到实验一观看# 1、实验目的&#xff1a; &#xff08;1&#xff09;掌握DHCP工作原理。 &#xff08;2&#xff09;配置路由器作为DHCP服务器。 &#xff08;3&#x…

手写体识别Tensorflow实现

简介&#xff1a;本文先讲解了手写体识别中涉及到的知识&#xff0c;然后分步讲解了代码的详细思路&#xff0c;完成了手写体识别案例的讲解&#xff0c;希望能给大家带来帮助&#xff0c;也希望大家多多关注我。本文是基于TensorFlow1.14.0的环境下运行的 手写体识别Tensorflo…

【SpringBoot】公共字段自动填充

问题引入 JavaEE开发的时候&#xff0c;新增字段&#xff0c;修改字段大都会涉及到创建时间(createTime)&#xff0c;更改时间(updateTime)&#xff0c;创建人(craeteUser)&#xff0c;更改人(updateUser)&#xff0c;如果每次都要自己去setter()&#xff0c;会比较麻烦&#…

【项目开发】为什么文件名要小写?

未经许可,不得转载。 文章目录 一、可移植性二、易读性三、易用性四、便捷性一、可移植性 Linux 系统对文件名大小写敏感,而 Windows 和 Mac 系统则不敏感。这种差异可能导致跨平台的问题。 例如,以下四个文件名: computerComPutercomPuterCOMPOTer在 Linux 系统上,它们…

ssm127基于SSM的乡镇篮球队管理系统+jsp(论文+源码)_kaic

毕 业 设 计&#xff08;论 文&#xff09; 题目&#xff1a;乡镇篮球队管理系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本乡镇篮球队管理…

C#获取视频第一帧_腾讯云媒体处理获取视频第一帧

一、 使用步骤&#xff1a; 第一步、腾讯云开启万象 第二步、安装Tencent.QCloud.Cos.Sdk 包 第三步、修改 腾讯云配置 图片存储目录配置 第四步、执行获取图片并保存 二、封装代码 using System.Text; using System.Threading.Tasks;using COSXML.Model.CI; using COSXML.A…

【数据分享】2003-2022年各省土地利用面积统计数据

数据介绍 2003-2022年各省土地利用面积统计数据数据时间2003-2008、2013、2015-2017、2019、2022数据类型excel数据指标土地调查面积/万公顷农用地面积/万公顷园林面积/万公顷牧草地面积/万公顷建设用地面积/万公顷居民点及工矿用地/万公顷交通用地/万公顷水利设施用地/万公顷…

任务调度工具Spring Test

Spring Task 是Spring框架提供的任务调度工具&#xff0c;可以按照约定的时间自动执行某个代码逻辑。 作用&#xff1a;定时自动执行某段Java代码 应用场景&#xff1a; 信用卡每月还款提醒 银行贷款每月还款提醒 火车票售票系统处理未支付订单 入职纪念日为用户发送通知 一.…

20 轮转数组

20 轮转数组 20.1 轮转数组解决方案 class Solution { public:void rotate(vector<int>& nums, int k) {int n nums.size();k k % n; // 如果 k 大于数组长度&#xff0c;取模减少不必要的旋转// 第一步&#xff1a;反转整个数组reverse(nums.begin(), nums.end(…

字符串相关题解

目录 字母异位词 最长公共前缀 博主主页&#xff1a;东洛的克莱斯韦克-CSDN博客 字母异位词 49. 字母异位词分组 - 力扣&#xff08;LeetCode&#xff09; 这道题更像一道语法题&#xff0c;考察对容器的掌握情况。如果按题目要求去模拟&#xff0c;不仅要分析每个字符串&am…

【微软:多模态基础模型】(3)视觉生成

欢迎关注【youcans的AGI学习笔记】原创作品 【微软&#xff1a;多模态基础模型】&#xff08;1&#xff09;从专家到通用助手 【微软&#xff1a;多模态基础模型】&#xff08;2&#xff09;视觉理解 【微软&#xff1a;多模态基础模型】&#xff08;3&#xff09;视觉生成 【微…

CentOS8 启动错误,enter emergency mode ,开机直接进入紧急救援模式,报错 Failed to mount /home 解决方法

先看现场问题截图&#xff1a; 1.根据提示 按 ctrld 输入 root 密码&#xff0c;进入系统。 2. 在紧急模式下运行&#xff1a;journalctl -xe &#xff0c;查看相关日志&#xff0c;找到关键点&#xff1a; Failed to mount /home 3.接着执行修复命令&#xff1a; xfs_repa…

2024140读书笔记|《作家榜名著:生如夏花·泰戈尔经典诗选》——你从世界的生命的溪流浮泛而下,终于停泊在我的心头

2024140读书笔记|《作家榜名著&#xff1a;生如夏花泰戈尔经典诗选》——你从世界的生命的溪流浮泛而下&#xff0c;终于停泊在我的心头 《作家榜名著&#xff1a;生如夏花泰戈尔经典诗选》[印]泰戈尔&#xff0c;郑振铎译&#xff0c;泰戈尔的诗有的清丽&#xff0c;有的童真&…

lenovo联想ThinkBook 14 G5 ABP(21JE)原装出厂Windows11系统恢复镜像包下载

适用机型 &#xff1a;【21JE】 链接&#xff1a;https://pan.baidu.com/s/1FUjwN8ZeaQ9qr3kNalSkYg?pwdqasf 提取码&#xff1a;qasf 联想原装出厂系统自带所有驱动、出厂主题壁纸、系统属性联机支持标志、系统属性专属LOGO标志、Office办公软件、联想电脑管家、联想浏览…

MySQL 数据类型

数值类型 int类型 类型说明tinyint1字节&#xff0c;范围从-128到127&#xff08;有符号&#xff09;&#xff0c;0到255&#xff08;无符号&#xff09;smallint2字节&#xff0c;范围从-2^15到2^15-1&#xff08;有符号&#xff09;&#xff0c;0到2^16-1&#xff08;无符号…

【WPF】Prism学习(三)

Prism Commands 1.复合命令&#xff08;Composite Commanding&#xff09; 这段内容主要介绍了在应用程序中如何使用复合命令&#xff08;Composite Commands&#xff09;来实现多个视图模型&#xff08;ViewModels&#xff09;上的命令。以下是对这段内容的解释&#xff1a; …

用go语言后端开发速查

文章目录 一、发送请求和接收请求示例1.1 发送请求1.2 接收请求 二、发送form-data格式的数据示例 用go语言发送请求和接收请求的快速参考 一、发送请求和接收请求示例 1.1 发送请求 package mainimport ("bytes""encoding/json""fmt""ne…

SpringCloud Alibaba入门简介和Nacos服务注册和配置中心

前面已经把spring cloud相关的组件都一一学了个遍,现在有点小佩服自己…本来计划今天周末好好出去玩一圈,天气太热了,39了都,还是在办公室学习吧,进行下面的springCloud Alibaba 学习吧…不废话了赶快进入正体 1. SpringCloud Alibaba入门简介 1.1 why会出现SpringCloud alib…