AVL树实现

目录

​编辑

一,AVL树的概念

二,实现AVL树(部分)

1.AVL树的节点

2.AVL数的插入

1.当根节点为nullptr时要执行如下代码:

2.当根节点不为nullptr时

1.当parent的_bf变为0时,parent之前的_bf的大小就是-1或者1如下:

2.当parent的平衡因子被更新为1或者-1时:

3.当parent的平衡因子更新后变成-2或者2时

三,一些其他函数

四,全部代码


一,AVL树的概念

首先我们得先知道AVL树是个啥,AVL树是个啥呢?百度一下你就知道:

在百度上的概念如上。根据AVL树的概念我们可以总结出AVL树有如下特点:

1.本身首先是一棵二叉搜索树。

2.带有平衡条件:每个结点的左右子树的高度之差的绝对值(平衡因子)最多为1。

二,实现AVL树(部分)

1.AVL树的节点

 首先我们根据AVL树的特点可以想到的AVL树节点成员有:

1.因为AVL树还是一棵二叉树,所以AVL树中必有指针left与right,而且还有一个变量表示节点值。

2.因为二AVL树需要记录高度差,所以在节点内就会有一个变量来记录高度差,这个变量便是平衡因子。并且这个变量的类型是int。

3.最后为了方便后续操作,我们的节点内还会存放一个指针叫做parent,这个节点指向当前节点的父节点。

所以写出的节点代码如下:

template <class K,class V>
	struct AVL_Node//节点
	{
		AVL_Node(pair<K,V>& key)
			:parent(nullptr)
			,_left(nullptr)
			,_right(nullptr)
			,_bf(0)
			,_key(key)
		{}

		AVL_Node<K, V>* parent;//父节点指针
		AVL_Node<K, V>* _left;//左孩子指针
		AVL_Node<K, V>* _right;//右孩子指针
		int _bf;//平衡因子
		pair<K, V>_key;//val
	};

2.AVL数的插入

AVL树的插入是我们这次实现的重头戏,在插入时要理解的重点内容便是:

1.插入时的旋转。

2.平衡因子的调整。

现在先来写在旋转前的代码:

1.当根节点为nullptr时要执行如下代码:
if (_root == nullptr)//当根节点为空时
{
	_root = new AVL_Node<K, V>(key);
}
2.当根节点不为nullptr时

1.先找到插入位置:

AVL_Node<K, V>* cur = _root;
AVL_Node<K, V>* parent = cur;
while (cur)
{
if (key < cur->_key)
{
  parent = cur;
  cur = cur->_left;
}
else if (key > cur->_key)
{
	parent = cur;
	cur = cur->_right;
}
else
{
	return false;
}
}						

这段寻找插入位置的代码和二叉搜索树的代码相同。

2.找到插入位置以后便开始进行插入

cur = new AVL_Node<K, V>(key);
if (key.first > parent->_key.first)
{
	parent->_right = cur;
}
	else
{
  parent->_left = cur;
}

 cur->_parent = parent;//更新父节点

3.调整上一个节点的平衡因子。依据:_bf = 右节点的高度--左节点的高度

所以当我插入左边时:

if (cur == parent->_left)//左边添加节点,parent的_bf减1
{
	parent->_bf--;
}

当我插入右边时:

else if (cur == parent->_right)//右边添加节点,parntt的高度加1
{
  parent->_bf++;
}

4.根据父节点的_bf的大小来决定是否需要往上调整

1.当parent的_bf变为0时,parent之前的_bf的大小就是-1或者1如下:

其实我的parent变成0以后AVL树的高度下降,所以对我的其它节点的平衡因子的大小是没有什么影响的,所以在这种情况下我便可以直接退出:

if (parent->_bf == 0)//子树高度不变不影响整棵树高度
{
  break;
}
2.当parent的平衡因子被更新为1或者-1时:

能变更新成这种情况,那我之前的平衡因子一定为0了。这时候,我整棵树的高度肯定是增加了,所以平衡因子必须改变所以代码如下:

else if (parent->_bf == 1 || parent->_bf == -1)//子树高度变了,但是在接受范围内,节点的平衡因子要做调整
{
   cur = parent;
  parent = parent->_parent;
}

这段代码是不全的,这里只展示了节点向上调整的操作,如果想看整段代码请往后看。

3.当parent的平衡因子更新后变成-2或者2时

这个时候就发生异常了,这个时候就要开始旋转了。这个时候的旋转又分为四种:

1.单纯的右边高

如以下情况:

可以看到在插入新的节点以后,树的高度发生了变化并且都是右边的高度高且出现了异常情况。所以这个时候就要发生左旋来调整高度,示意图如下:

根据这个过程写出如下代码:

void Leftuniflex(AVL_Node<K, V>* parent)
	{
        //记录要旋转的节点
		AVL_Node<K, V>* subR = parent->_right;
		AVL_Node<K, V>* subRL = subR->_left;

        //旋转
		parent->_right = subRL;
		subR->_left = parent;

		//更新父节点
		AVL_Node<K, V>* ppNode = parent->_parent;
		parent->_parent = subR;
		if (subRL)
			subRL->_parent = parent;

		if (parent == _root)
		{
			_root = subR;
		}
		 
		subR->_parent = ppNode;

		//更新平衡因子
		parent->_bf = subR->_bf = 0;
	}

因为在示意图里没有显示父节点的更新,所以在这里要注意对于父节点的更新。

2.单纯的左边高

如下情况:

看到这个示意图是否有一种似曾相识的感觉呢?其实单纯的左边高的旋转操作方法和单纯右边高的操作方法一样,只不过旋转的方向改变了。根据示意图写出代码如下:
 

void Rightuniflex(AVL_Node<K, V>* parent)
	{
		//旋转
		AVL_Node<K, V>* subL = parent->_left;
		AVL_Node<K, V>* subLR = subL->_right;
		parent->_left = subLR;
		subL->_right = parent;

		//更新父节点
		AVL_Node<K, V>* ppNode = parent->_parent;
		parent->_parent = subL;
		if (subLR)
			subLR->_parent = parent;

		if (parent == _root)
		{
			_root = subL;
		}

		subL->_parent = ppNode;

		//更新平衡因子
		parent->_bf = subL->_bf = 0;
	}

3.右边整体高但是局部矮

解决方法:1.将整棵AVL树通过右单旋调整成单纯的右边高然后再将整棵树通过左单旋调整成平衡的AVL树。比如如以下情况:

一,新增节点插入到60的右节点b处,此时60的_bf为-1

在这种情况下,首先便要以90这个节点为parent进行右单旋让整棵树变成单纯的右边高。如下图所示:

在转化为单纯的右边高以后,便可以执行第二步了。第二步的操作便是和左旋操作是一样的,如图所示:

二,新增节点插入到60的右节点c处,此时60的_bf为1

画出示意图如下:

然后还是一样的操作将这棵树变成单纯的右边高:

然后继续执行第二步,也是单纯的左旋操作:

三,当h为0时,新增节点为60。此时60的_bf = 0

在这个时候还是一样的操作:先变成单纯的右边高

然后再操作:

再右边整体高,局部左边高的节点插入情况便是以上三种。其实这些情况的旋转操作都是一样的但是对应的平衡因子_bf的更新是不一样的。

根据以上情况写出代码如下:

//右左双旋
	void RLuniflex(AVL_Node<K, V>* parent)
	{
		AVL_Node<K, V>* subR = parent->_right;
		AVL_Node<K, V>* subRL = subR->_left;
		int bf = subRL->_bf;

		Rightuniflex(subR);//先局部右旋
		Leftuniflex(parent);//再整体左旋


		//更新平衡因子
		if (bf == 0)//subRL自己就是新增
		{
			parent->_bf = subR->_bf = subRL->_bf = 0;
		}

		else if (bf == -1)
		{
			subR->_bf = 1;
			parent->_bf = subRL->_bf = 0;
		}

		else if (bf == 1)
		{
			parent->_bf = -1;
			subRL->_bf = subR->_bf = 0;
		}
		else
		{
			assert(false);
		}
		
	}

4.当左边整体高但是右边局部高时:

在这种情况下的分析方法和《当右边整体高但是左边局部高》的分析方法相同。也分为三步:

1.现局部左旋让AVL树变成单纯的左边高。

2.再整体右旋调整树的平衡。

3.调整平衡因子。

分析方法和上述代码相同,在这里就直接写代码了:

//左右双旋(狗日的平衡因子,让我搞了半天,哭了)
	void LRuniflex(AVL_Node<K, V>* parent)
	{
		AVL_Node<K, V>* subL = parent->_left;
		AVL_Node<K, V>* subLR = subL->_right;
		int bf = subLR->_bf;

		Leftuniflex(subL);//先局部左旋
		Rightuniflex(parent);//再整体右旋


		//更新平衡因子
		if (bf == 0)//subRL自己就是新增
		{
			parent->_bf = subL->_bf = subLR->_bf = 0;
		}

		else if (bf == -1)
		{
			subL->_bf = 0;
			parent->_bf = 1;
		    subLR->_bf = 0;
		}

		else if (bf == 1)
		{
			parent->_bf = 0;
			subLR->_bf = 0;
			subL->_bf = -1;
		}
		else
		{
			assert(false);
		}

	}

兄弟们在写代码时千万不要和我一样粗心,找bug找了好久!!!!

三,一些其他函数

1.中序遍历函数

void Inorder()
{
  _Inorder(_root);
}
void _Inorder(AVL_Node<K, V>* root)
{
	if (root == nullptr)
	{
		return;
	}

  _Inorder(root->_left);
  cout << root->_key.first << " ";
 _Inorder(root->_right);
}

记住,AVL树的中序遍历也是一个有序的数列。

2.判断平衡的函数

int Height(AVL_Node<K, V>* root)
		{
			if (root == nullptr)
			{
				return 0;
			}

			int L = Height(root->_left);
			int R = Height(root->_right);

			return L > R ? L + 1 : R + 1;
		}

		bool _Isblance(AVL_Node<K, V>* root)
		{
			if (root == nullptr)
			{
				return true;
			}

			int R = Height(root->_right);
			int L = Height(root->_left);
			int bf = root->_bf;

			if (R - L!=bf)
			{
				cout << "R-L:" << R - L << endl;
				cout << root->_key.first << "->" << root->_bf <<"平衡因子异常" << endl;
				return false;
			}

			return (abs(R-L)<2)&& _Isblance(root->_left) && _Isblance(root->_right);
			 
		}

四,全部代码

#include<iostream>
#include<assert.h>
using namespace std;
using std::pair;

namespace AVL
{
	template <class K,class V>
	struct AVL_Node//节点
	{
		AVL_Node(pair<K,V>& key)
			:_parent(nullptr)
			,_left(nullptr)
			,_right(nullptr)
			,_bf(0)
			,_key(key)
		{}

		AVL_Node<K, V>* _parent;
		AVL_Node<K, V>* _left;
		AVL_Node<K, V>* _right;
		int _bf;
		pair<K, V>_key;
	};


	template <class K, class V>
	class AVL_Tree
	{
	public:
		bool insert( pair<K, V> key)
		{
			if (_root == nullptr)//当根节点为空时
			{
				_root = new AVL_Node<K, V>(key);
			}

			else//当根节点不为空时
			{
				AVL_Node<K, V>* cur = _root;
				AVL_Node<K, V>* parent = cur;
				while (cur)
				{
					if (key.first < cur->_key.first)
					{
						parent = cur;
						cur = cur->_left;
					}
					else if (key.first > cur->_key.first)
					{
						parent = cur;
						cur = cur->_right;
					}
					else
					{
						return false;
					}
				}

				cur = new AVL_Node<K, V>(key);
				if (key.first > parent->_key.first)
				{
					parent->_right = cur;
				 }
				else
				{
					parent->_left = cur;
				}

				cur->_parent = parent;

				while (parent)
				{
					if (cur == parent->_left)//左边添加节点,parent的高度减1
					{
						parent->_bf--;
					}
					else if (cur == parent->_right)//右边添加节点,parent的高度加1
					{
						parent->_bf++;
					}

					if (parent->_bf == 0)//子树高度不变不影响整棵树高度
					{
						break;
					}
					else if (parent->_bf == 1 || parent->_bf == -1)//子树高度变了,但是在接受范围内,节点的平衡因子要做调整
					{
						cur = cur->_parent;
						parent = parent->_parent;
					}
					else if (parent->_bf == 2 || parent->_bf == -2)
					{
						//Inorder();
						//出现异常该旋转了
						//单纯右边高,发生左单旋
						if (parent->_bf == 2 && cur->_bf == 1)
						{
							 Leftuniflex(parent);
						}
						//单纯左边高,发生右单旋
						else if (parent->_bf == -2 && cur->_bf == -1)
						{
							 Rightuniflex(parent);

						}
						//右边整体高,局部矮
						else if (parent->_bf == 2 && cur->_bf == -1)
						{
							RLuniflex(parent);
						}
						//左边整体高,局部矮
						else if (parent->_bf == -2 && cur->_bf == 1)
						{
							LRuniflex(parent);
						}
					    
						else
						{
							assert(false);
						}
						break;
					}

					else
					{
						assert(false);
					}
			
				}
			}
				return true;
	    }

	void Leftuniflex(AVL_Node<K, V>* parent)
	{
		AVL_Node<K, V>* subR = parent->_right;
		AVL_Node<K, V>* subRL = subR->_left;
		parent->_right = subRL;
		subR->_left = parent;

		//更新父节点
		AVL_Node<K, V>* ppNode = parent->_parent;
		parent->_parent = subR;
		if (subRL)
			subRL->_parent = parent;

		if (parent == _root)
		{
			_root = subR;
		}
		 
		else
		{
			if (parent == ppNode->_left)
			{
				ppNode->_left = subR;
			}
			else
			{
				ppNode->_right = subR;
			}
		}
		subR->_parent = ppNode;

		//更新平衡因子
		parent->_bf = subR->_bf = 0;
	}


	void Rightuniflex(AVL_Node<K, V>* parent)
	{
		//旋转
		AVL_Node<K, V>* subL = parent->_left;
		AVL_Node<K, V>* subLR = subL->_right;
		parent->_left = subLR;
		subL->_right = parent;

		//更新父节点
		AVL_Node<K, V>* ppNode = parent->_parent;
		parent->_parent = subL;
		if (subLR)
			subLR->_parent = parent;

		if (parent == _root)
		{
			_root = subL;
		}

		else
		{
			if (parent == ppNode->_left)
			{
				ppNode->_left = subL;
			}
			else
			{
				ppNode->_right = subL;
			}
		}
		subL->_parent = ppNode;

		//更新平衡因子
		parent->_bf = subL->_bf = 0;
	}

	//右左双旋
	void RLuniflex(AVL_Node<K, V>* parent)
	{
		AVL_Node<K, V>* subR = parent->_right;
		AVL_Node<K, V>* subRL = subR->_left;
		int bf = subRL->_bf;

		Rightuniflex(subR);//先局部右旋
		Leftuniflex(parent);//再整体左旋


		//更新平衡因子
		if (bf == 0)//subRL自己就是新增
		{
			parent->_bf = subR->_bf = subRL->_bf = 0;
		}

		else if (bf == -1)
		{
			subR->_bf = 1;
			parent->_bf = subRL->_bf = 0;
		}

		else if (bf == 1)
		{
			parent->_bf = -1;
			subRL->_bf = subR->_bf = 0;
		}
		else
		{
			assert(false);
		}
		
	}

	//左右双旋(狗日的平衡因子,让我搞了半天,哭了)
	void LRuniflex(AVL_Node<K, V>* parent)
	{
		AVL_Node<K, V>* subL = parent->_left;
		AVL_Node<K, V>* subLR = subL->_right;
		int bf = subLR->_bf;

		Leftuniflex(subL);//先局部左旋
		Rightuniflex(parent);//再整体右旋


		//更新平衡因子
		if (bf == 0)//subRL自己就是新增
		{
			parent->_bf = subL->_bf = subLR->_bf = 0;
		}

		else if (bf == -1)
		{
			subL->_bf = 0;
			parent->_bf = 1;
		    subLR->_bf = 0;
		}

		else if (bf == 1)
		{
			parent->_bf = 0;
			subLR->_bf = 0;
			subL->_bf = -1;
		}
		else
		{
			assert(false);
		}

	}


	void Inorder()
	{
		_Inorder(_root);
	}

	void Isblance()
	{
		_Isblance(_root);
	}
	 


	private:

		void _Inorder(AVL_Node<K, V>* root)
		{
			if (root == nullptr)
			{
				return;
			}

			_Inorder(root->_left);
			cout << root->_key.first << " ";
			_Inorder(root->_right);
		}

		int Height(AVL_Node<K, V>* root)
		{
			if (root == nullptr)
			{
				return 0;
			}

			int L = Height(root->_left);
			int R = Height(root->_right);

			return L > R ? L + 1 : R + 1;
		}

		bool _Isblance(AVL_Node<K, V>* root)
		{
			if (root == nullptr)
			{
				return true;
			}

			int R = Height(root->_right);
			int L = Height(root->_left);
			int bf = root->_bf;

			if (R - L!=bf)
			{
				cout << "R-L:" << R - L << endl;
				cout << root->_key.first << "->" << root->_bf <<"平衡因子异常" << endl;
				return false;
			}

			return (abs(R-L)<2)&& _Isblance(root->_left) && _Isblance(root->_right);
			 
		}

		AVL_Node<K, V>* _root = nullptr;
	};

}

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

十、Linux运行级别

1.基本介绍 运行级别说明&#xff1a; 0&#xff1a;关机 1&#xff1a;单用户【找回丢失密码】 2&#xff1a;多用户状态没有网络服务 【非常少】 3&#xff1a;多用户状态有网络服务 【最多】 4&#xff1a;系统未使用保留给用户 5&#xff1a;图形界面【Linux一启动自动进入…

lambda表达式c++

介绍 可调用对象 对于一个表达式&#xff0c;如果可以对其使用调用运算符&#xff08;&#xff09;&#xff0c;则称它为可调用对象。如函数就是一个可调用对象&#xff0c;当我们定义了一个函数f(int)时&#xff0c;我们可以通过f(5)来调用它。 可调用对象有&#xff1a; …

【Redis】zset常用命令集合间操作内部编码使用场景

文章目录 前置知识列表、集合、有序集合三者的异同点 普通命令ZADDZCARDZCOUNTZRANGEZREVRANGEZRANGEBYSCOREZPOPMAXBZPOPMAXZPOPMINBZPOPMINZRANKZREVRANKZSCOREZREMZREMRANGEBYRANKZREMRANGEBYSCOREZINCRBY 集合之间的操作ZINTERSTOREZUNIONSTORE 命令小结内部编码测试内部编…

滑动窗口练习(一)— 固定窗口最大值问题

题目 假设一个固定大小为W的窗口&#xff0c;依次划过arr&#xff0c; 返回每一次滑出状况的最大值 例如&#xff0c;arr [4,3,5,4,3,3,6,7], W 3 返回&#xff1a;[5,5,5,4,6,7] 暴力对数器 暴力对数器方法主要是用来做校验&#xff0c;不在乎时间复杂度&#xff0c;逻辑上…

GitHub如何删除仓库

GitHub如何删除仓库 删除方法第一步第二步第三步 删除方法 第一步 在仓库的界面选择Settings 第二步 选择General,页面拉到最后。 第三步 删除仓库。

七,vi和vim

Linux系统会内置vi文本编辑器 Vim具有程序编辑的能力&#xff0c;可以看做是Vi的增强版本&#xff0c;可以主动的以字体颜色辨别语法的正确性&#xff0c;方便程序设计。代码补完、编译及错误跳转等方便编程的功能特别丰富&#xff0c;在程序员中被广泛使用。 vi和vim常用的三…

CICD 持续集成与持续交付——gitlab

部署 虚拟机最小需求&#xff1a;4G内存 4核cpu 下载&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/ 安装依赖性 [rootcicd1 ~]# yum install -y curl policycoreutils-python openssh-server perl[rootcicd1 ~]# yum install -y gitlab-ce-15.9.3-ce.0…

LinkWeChat V4.9.8 版本发布

LinkWeChat v4.9.8 已经发布&#xff0c;基于企业微信的 SCRM 系统 LinkWeChat 是国内首个基于企业微信的开源 SCRM&#xff0c;在集成了企微强大的开放能力的基础上&#xff0c;进一步升级拓展灵活高效的客户运营能力及多元化精准营销能力&#xff0c;让客户与企业之间建立强…

Java概述

接触Java后会发现它的体系有一个特点&#xff0c;就是非常喜欢用“J”字母开头的缩写&#xff0c;比如JCP, JSR, JMS, JPA, JSP, JAX-RS......它们有些是规范&#xff0c;有些是组织的名称&#xff0c;表意多样&#xff0c;对第一次接触的人来说很可能会觉得混乱&#xff0c;本…

快速修改ppt | 显得不单调

做完ppt&#xff0c;怎样不显得单调 ----> 加个 主题&#xff0c;首页 改下 字体&#xff08;如 华文行楷&#xff0c;96&#xff0c;字体颜色&#xff09;

开拓经验专栏:从十来天的晨型人体验开始

文章目录 拓新缘起契机实践心得 拓新 确定要新开一个板块&#xff0c;用来记录持续自我提升的经验和教训&#xff0c;着实遭遇了不少阻力。 首先&#xff0c;我的语文功底一向不行&#xff0c;当年高考前&#xff0c;语文分数在及格线上下跳动都是常事&#xff0c;现在却要通…

git使用及常用命令

在初入公司中&#xff0c;若使用的是git管理工具&#xff0c;需要做以下步骤&#xff1a; 1&#xff0c;常用命令在&#xff1a; &#xff08;1&#xff09;&#xff0c;git config --global user.name xxx(名字) //若不设置 那么下次提交代码时会报错 其次该设置名字和…

从零开始:Rust环境搭建指南

大家好&#xff01;我是lincyang。 今天&#xff0c;我们将一起探讨如何从零开始搭建Rust开发环境。 Rust环境搭建概览 Rust是一种系统编程语言&#xff0c;以其安全性、并发性和性能闻名。搭建Rust环境是学习和使用这一语言的第一步。 第一步&#xff1a;安装Rust Rust的…

二维码智慧门牌管理系统升级解决方案:查询功能大提升,让地址查找变得轻松便捷!

文章目录 前言一、支持地址名称、小区等信息进行模糊查询二、支持地图上绘制多边形、圆形、矩形进行范围查询三、高效的数据处理能力&#xff0c;保证查询速度四、灵活的应用场景&#xff0c;满足多种需求 前言 随着科技的快速发展和城市化的加速推进&#xff0c;传统的门牌管…

二叉树oj题集(LeetCode)

100. 相同的树 关于树的递归问题&#xff0c;永远考虑两方面&#xff1a;返回条件和子问题 先考虑返回条件&#xff0c;如果当前的根节点不相同&#xff0c;那就返回false&#xff08;注意&#xff0c;不要判断相等时返回什么&#xff0c;因为当前相等并不能说明后面节点相等…

常用组合逻辑verilog实现之8-3优先编码器

文章目录 一、问题描述二、verilog源码三、综合及仿真结果一、问题描述 本例中将实现一个8-3优先编码器。优先编码器允许多个输入信号同时有效,输出针对优先级别高的信号进行编码。 8-3优先编码器有对应的芯片实现比如TI公司的CD4532,可以从下面链接下载其手册。 CD4532数…

论文-分布式-拜占庭将军问题

目录 0-前言 1-导引 2-不可能性 3将军(1叛徒)问题不存在解/不能达成共识 少于3m1个将军(有m个叛徒)不存在解/不能达成共识 精确一致性与近似一致性是同等困难的 3-使用口头消息的解 “口头消息”的含义 OM(m)算法的步骤 OM(m)算法的正确性推导 4-使用签名消息情况下…

传奇手游白日门【龙城霸业】win服务端+双端+GM后台+详细教程

搭建资源下载地址&#xff1a;传奇手游白日门【龙城霸业】win服务端双端GM后台详细教程-海盗空间

【实习】串口通信

modbus介绍 详解Modbus通信协议—清晰易懂 Modbus协议是一个master/slave架构的协议。有一个节点是master节点&#xff0c;其他使用Modbus协议参与通信的节点是slave节点。每一个slave设备都有一个唯一的地址。在串行和MB网络中&#xff0c;只有被指定为主节点的节点可以启动一…

SpringBoot——入门及原理

SpringBoot用来简化Spring应用开发&#xff0c;约定大于配置&#xff0c;去繁从简&#xff0c;是由Pivotal团队提供的全新框架。其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置&#xff08;有特殊需求可以添加自己的配置覆盖默认配…
最新文章