map,set的封装(基于改造红黑树)

news2024/11/19 16:39:20

 目录

引言

1.迭代器

2.map的[]重载

3.KeyOfValue模板参数

4.整体代码展示

 


  

//改造后的红黑树代码
#include <iostream>
using namespace std;
 
enum Colour {
	RED = 0,
	BLACK,
};
 
//为了实现map与set封装使用同一个模板红黑树,前者的value是pair,后者的value为key
//因此我们需要对红黑树的模板进行改造
template<class T>
struct RBTreeNode {
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
 
	T _data;
	Colour _col;
 
	//节点构造函数
	RBTreeNode(const T& data)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _data(data)
		, _col(RED)
	{}
};
 
 
//树的迭代器构造
template<class T, class Ref, class Ptr>
struct __RBtree_iterator
{
	typedef RBTreeNode<T> Node;
	typedef __RBtree_iterator<T, Ref, Ptr> self;
	Node* _node;
 
	__RBtree_iterator() {}
	__RBtree_iterator(Node* node)
		:_node(node)
	{}
 
	// 1、typedef __RBTreeIterator<T, T&, T*> iterator;  拷贝构造
	// 2、 typedef __RBTreeIterator<T, const T&, const T*> const_iterator;
	//  支持普通迭代器构造const迭代器的构造函数
 
	__RBtree_iterator(const __RBtree_iterator<T, T&, T*>& it)
		:_node(it._node)
	{}
 
	//实现前置++,保证指针往后移动,能够实现树的中序遍历
	self& operator++()
	{
		//如果当前节点的右子树不为空
		if (_node->_right)
		{
			// 1、右不为空,下一个就是右子树的最左节点
			Node* subLeft = _node->_right;
			while (subLeft->_left)
			{
				subLeft = subLeft->_left;
			}
 
 
			_node = subLeft;
		}
//如果当前节点的右子树为空,由于按照左子树,根,右子树遍历,说明此时该子树已经全部遍历
		//需要向上找孩子为父亲左孩子的祖先节点
		else
		{
			Node* parent = _node->_parent;
			Node* cur = _node;
 
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = parent->_parent;
			}
 
			_node = parent;
		}
 
		return *this;
	}
 
	//实现前置--
	self& operator--()
	{
 
		//如果当前节点的左子树不为空
		if (_node->_left)
		{
			// 1、左不为空,下一个就是左子树的最右节点
			Node* subRight = _node->_left;
			while (subRight->_right)
			{
				subRight = subRight->_right;
			}
 
 
			_node = subRight;
		}
		// 2、左为空,孩子是父亲的右的那个祖先
		else
		{
			Node* parent = _node->_parent;
			Node* cur = _node;
 
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = parent->_parent;
			}
 
			_node = parent;
		}
 
		return *this;
	}
 
	Ref operator*()
	{
		return _node->_data;
	}
 
	Ptr operator->()
	{
		return &_node->_data;
	}
 
	bool operator!=(const self& s)
	{
		return _node != s._node;
	}
};
 
//仿函数
template<class K, class T, class KeyOfValue>
class RBTree
{
	typedef RBTreeNode<T> Node;
public:
	~RBTree()
	{
		_Destroy(_root);
		_root = nullptr;
	}
 
public:
	typedef __RBtree_iterator<T, T&, T*> iterator;
	typedef __RBtree_iterator<T, const T&, const T*> const_iterator;
 
	iterator begin()
	{
		Node* cur = _root;
		//找树的最左节点
		while (cur && cur->_left)
		{
			cur = cur->_left;
		}
 
		return iterator(cur);
	}
 
	const_iterator begin() const
	{
		Node* cur = _root;
		//找树的最左节点
		while (cur && cur->_left)
		{
			cur = cur->_left;
		}
 
		return iterator(cur);
	}
 
	iterator end()
	{
		return iterator(nullptr);
	}
 
	const_iterator end() const
	{
		return const_iterator(nullptr);
	}
 
	//对于set类型而言,其value比较的是key,所以增加了一个模板参数
	Node* Find(const K& key)
	{
		Node* cur = _root;
		KeyOfValue kot;
		while (cur)
		{   
			if (kot(cur-> _data) > key)
			{
				cur = cur->_left;
			}
			else if (kot(cur-> _data) < key)
			{
				cur = cur->_right;
			}
			else
			{
				return cur;
			}
		}
 
		return nullptr;
	}
 
	pair<iterator,bool> Insert(const T& data)
	{
		//假如刚开始没有节点,直接使其成为根即可
		//满足规则,根节点必须为黑色
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			return make_pair(iterator(_root), true);
		}
 
		//同样满足二叉树的搜索规则,先找到新节点的正确位置
		Node* parent = nullptr;
		Node* cur = _root;
		KeyOfValue kot;
		while (cur)
		{
			if (kot(cur-> _data) > kot(data))
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (kot(cur-> _data) < kot(data))
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return make_pair(iterator(cur),false);
			}
		}
 
		//建新节点
		cur = new Node(data);
		Node* newnode = cur;
		if (kot(parent->_data) > kot(data))
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
		cur->_parent = parent;
 
		//调整节点颜色
		while (parent && parent->_col == RED)
		{
			//找爷爷
			Node* grandfather = parent->_parent;
			//父亲为爷爷的左节点
			if (grandfather->_left == parent)
			{
				//则叔叔是爷爷的右节点
				Node* uncle = grandfather->_right;
				// 情况1:u存在且为红,变色处理,并继续往上处理
				if (uncle && uncle->_col == RED)
				{
					//父亲和叔叔节点都调节为黑色
					parent->_col = BLACK;
					uncle->_col = BLACK;
					//爷爷调节为红色
					grandfather->_col = RED;
 
					//往上调节
					cur = grandfather;
					parent = cur->_parent;
				}
				else // 情况2+3:u不存在/u存在且为黑,旋转+变色
				{
					//     g
					//   p   u
					// c
					//右单旋
					if (cur == parent->_left)
					{
						RotateR(grandfather);
						grandfather->_col = RED;
						parent->_col = BLACK;
					}
					else
					{
						//    g
					   //   p   u
					  //      c
						//LR双旋
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						//parent->_col = RED;
						grandfather->_col = RED;
					}
 
					break;
				}
			}
			//父亲为爷爷的右节点
			else
			{
				//则叔叔是爷爷的左节点
				Node* uncle = grandfather->_left;
				// 情况1:u存在且为红,变色处理,并继续往上处理
				if (uncle && uncle->_col == RED)
				{
					//父亲和叔叔节点都调节为黑色
					parent->_col = BLACK;
					uncle->_col = BLACK;
					//爷爷调节为红色
					grandfather->_col = RED;
 
					//往上调节
					cur = grandfather;
					parent = cur->_parent;
				}
				else // 情况2+3:u不存在/u存在且为黑,旋转+变色
				{
					//     g
					//   u   p
					//        c
					//左单旋
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						grandfather->_col = RED;
						parent->_col = BLACK;
					}
					else
					{
						//     g
						//   u   p
						//     c
						//RL双旋
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						//parent->_col = RED;
						grandfather->_col = RED;
					}
 
					break;
				}
			}
		}
 
		_root->_col = BLACK;
 
		return make_pair(iterator(newnode),true);
	}
 
	int Height()
	{
		return _Height(_root);
	}
 
	bool IsRBTree()
	{
		//假如根节点存在,但颜色不是黑色,则不是红黑树
		if (_root && _root->_col == RED)
		{
			cout << "根节点颜色是红色" << endl;
			return false;
		}
 
		//随便选一条路径作为黑色节点参考点
		int benchmark = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
				benchmark++;
			cur = cur->_left;
		}
 
		// 连续红色节点
		return _Check(_root, 0, benchmark);
	}
 
private:
	void _Destroy(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
 
		_Destroy(root->_left);
		_Destroy(root->_right);
		delete root;
	}
 
	int _Height(Node* root)
	{
		if (root == NULL)
			return 0;
 
		int leftH = _Height(root->_left);
		int rightH = _Height(root->_right);
 
		return leftH > rightH ? leftH + 1 : rightH + 1;
	}
 
	bool _Check(Node* root, int blackNum, int benchmark)
	{
		//假如到空节点(叶子节点),说明已经走完一条路径,可以开始判断
		if (root == nullptr)
		{
			//假如统计出的黑色节点个数和参考黑色节点个数不同,则一定不是红黑树
			if (blackNum != benchmark)
			{
				cout << "某条路径黑色节点的数量不相等" << endl;
				return false;
			}
 
			return true;
		}
 
		//递归遇到黑色节点时,则blackNum可以加1
		if (root->_col == BLACK)
			blackNum++;
 
//假如连续存在两个红色节点,则也不是红黑树,注意还需要判断父节点是否存在
		if (root->_col == RED && root->_parent && root->_parent->_col == RED)
		{
			cout << "存在连续的红色节点" << endl;
			return false;
		}
 
		//递归判断是否是红黑树,左子树和右子树都为红黑树,则为红黑树
		return _Check(root->_left, blackNum, benchmark)
			&& _Check(root->_right, blackNum, benchmark);
	}
 
	//左旋
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
 
		//b变成30的右
		parent->_right = subRL;
		//父节点也需要调整,但subRL可能为空
		if (subRL)
			subRL->_parent = parent;
 
//调整时未必是整棵树的调整,所以还需要考虑parent的链接问题,因此需要先记录ppNode
		Node* ppNode = parent->_parent;
 
		subR->_left = parent;
		parent->_parent = subR;
 
		if (ppNode == nullptr)
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else
		{
			//在调整爷爷节点指向的时候,还需要考虑原来parent是爷爷的左还是右
			//subR重新链接回爷爷的左或者右
			if (ppNode->_right == parent)
			{
				ppNode->_right = subR;
			}
			else
			{
				ppNode->_left = subR;
			}
 
			subR->_parent = ppNode;
		}
	}
 
	//右旋
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
 
		//b变成60的左
		parent->_left = subLR;
		//父节点也需要调整,但subRL可能为空
		if (subLR)
			subLR->_parent = parent;
 
//调整时未必是整棵树的调整,所以还需要考虑parent的链接问题,因此需要先记录ppNode
		Node* ppNode = parent->_parent;
 
		subL->_right = parent;
		parent->_parent = subL;
 
		if (ppNode == nullptr)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			//在调整爷爷节点指向的时候,还需要考虑原来parent是爷爷的左还是右
			//subL重新链接回爷爷的左或者右
			if (ppNode->_right == parent)
			{
				ppNode->_right = subL;
			}
			else
			{
				ppNode->_left = subL;
			}
 
			subL->_parent = ppNode;
		}
	}
 
	void _Inorder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
 
		_Inorder(root->_left);
		cout << root->_kv.first << " ";
		_Inorder(root->_right);
	}
private:
	Node* _root = nullptr;
};

引言

在上节,我们根据源码,对红黑树代码进行了改造,在这基础上,我们便可以利用同一份红黑树代

码,模拟实现map,set的封装

1.迭代器

set对应key模型,即在不在

一旦插入后,我们是不能够通过迭代器对它的key随意进行修改的

//源码中set的代码
typedef typename rep_type::const_iterator iterator;
typedef typename rep_type::const_iterator const_iterator;

 源码实现不能通过迭代器修改key值得方式也非常直接明了,即无论是const迭代器或者普通迭代 

器,对于set来说,实际上都是const迭代器,我们无法通过const迭代器对key进行修改

与之对应,map则可以通过迭代器修改value值

//源码中map的代码
typedef typename rep_type::iterator iterator;
typedef typename rep_type::const_iterator const_iterator;

对于map来说,是存在const迭代器和普通迭代器的区别的

2.map的[]重载

在map中,有一个元素访问操作非常特殊,也非常方便,我们模拟实现不能错过,那就是[]重载 

它主要能发挥四大作用

第一.(插入)如果红黑树中没有对应的值Key,它可以直接插入

第二.(插入+修改)如果红黑树中不存在对应的键值Key,它可以直接插入,并修改对应的Value 

第三.(查找)直接判断红黑树中是否存在对应的键值Key

第四.(修改)如果红黑树中存在对应的Key,可以直接修改对应的Value

using namespace std;
int main()
{   
	std::map<char, std::string> mymap;

	mymap['a'] = "an element";        //插入+修改
	mymap['b'] = "another element";   
	mymap['a'] = "an element!";        //修改
	mymap['c'] = mymap['b']; //赋值

	std::cout << "mymap['a'] is " << mymap['a'] << '\n';
	std::cout << "mymap['b'] is " << mymap['b'] << '\n';
	std::cout << "mymap['c'] is " << mymap['c'] << '\n';
	std::cout << "mymap['d'] is " << mymap['d'] << '\n';  //插入

	std::cout << mymap['a'] << endl;  //查找
	std::cout << "mymap now contains " << mymap.size() << " elements.\n";
	return 0;
}

对应的代码允许结果如下:

 

想要实现相应[ ]运算符重载,首先需要对insert进行改造

insert函数的返回值,不再是bool值,而是pair,包含迭代器和bool两大部分

假如插入失败,除了返回false外,还需要返回树中已经存在相同键值的迭代器

假如插入成功,除了返回true外,还需要返回插入的新节点的迭代器

通过迭代器,我们就可以相应访问节点的值,如果加一个引用,就可以对其进行修改

//方括号重载
V& operator[](const K& key)
{
	pair<iterator,bool> ret = _t.Insert(make_pair(key,V()));
	//插入后,返回该位置对应的迭代器,我们需要返回的是
	return ret.first->second;
}

pair<iterator, bool> insert(const pair<const K, V>& kv)
{
	return _t.Insert(kv);
}

3.KeyOfValue模板参数

在红黑树插入insert,查找Find中,我们都需要比较两个键值的大小,从而找到对应节点的位置

但是set,map的data值可并不相同,前者是key,后者则是pair

如果直接和key比较,假如是set则没有问题,但是假如是map,则会出现毛病

原因在于pair的比较规则不符合我们的要求,pair 在first member(第一个参数)相同的情况下,还会

去比较second member(第二个参数),这显然不太符合我们要求,因为我们红黑树建树,是只比较

Key的

所以,我们引入了KeyOfValue这个模板参数,然后在map,set封装时,提供对应的仿函数,取出对

应应该比较的值

class map {
		struct KeyOfValue
		{
			const K& operator()(const pair<const K,V>& kv)
			{
				return kv.first;
			}
		};
...
}


 class set {
	   struct KeyOfValue 
	   {
		   const K& operator()(const K& key)
		   {
			   return key;
		   }
	   };
...
}

4.整体代码展示

//set模拟封装
#include "Tree.h"
namespace zzq 
{
   template<class K>
   class set {
	   struct KeyOfValue 
	   {
		   const K& operator()(const K& key)
		   {
			   return key;
		   }
	   };

   public:
  typedef typename RBTree<K, K, KeyOfValue>::const_iterator iterator;
  typedef typename RBTree<K, K, KeyOfValue>::const_iterator const_iterator;

	   iterator begin()
	   {
		   return _t.begin();
	   }

	   iterator end()
	   {
		   return _t.end();
	   }
       	const_iterator begin()const
		{
			return _t.begin();
		}
		const_iterator end()const
		{
			return _t.end();
		}
	   pair<iterator, bool> insert(const K& key)
	   {
		   return _t.Insert(key);
	   }
   private:
	   RBTree<K, K, KeyOfValue> _t;
   };
}
//map模拟封装
#include "Tree.h"
namespace zzq
{
	template<class K,class V>
	class map {
		struct KeyOfValue
		{
			const K& operator()(const pair<const K,V>& kv)
			{
				return kv.first;
			}
		};

	public:
 typedef typename RBTree<K, pair<const K, V>, KeyOfValue>::iterator iterator;

		iterator begin()
		{
			return _t.begin();
		}

		iterator end()
		{
			return _t.end();
		}
	    const_iterator begin()const
		{
			return _t.begin();
		}
		const_iterator end()const
		{
			return _t.end();
		}

		//方括号重载
		V& operator[](const K& key)
		{
			pair<iterator,bool> ret = _t.Insert(make_pair(key,V()));
			//插入后,返回该位置对应的迭代器,我们需要返回的是
			return ret.first->second;
		}

		pair<iterator, bool> insert(const pair<const K, V>& kv)
		{
			return _t.Insert(kv);
		}
	private:
		RBTree<K, pair<const K, V>, KeyOfValue> _t;
	};

}

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

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

相关文章

数学分析:外微分

先回顾下微分的概念&#xff0c;首先我们找到一个道路x&#xff0c;它是关于时间t的函数&#xff0c;然后我们可以得到一个速度&#xff0c;也就是切向量&#xff0c;所有道路的切向量组成了切空间。如果从泛函角度来理解&#xff0c;它应该是一个求偏导的基。是一个向量。而微…

Python语法(二、内置函数)

数学计算库 Python ​math ​模块提供了许多对浮点数的数学运算函数。 Python ​cmath ​模块包含了一些用于复数运算的函数。 import math 内置函数 关键字 自定义函数 Python函数的定义。定义函数需要用def 关键字实现&#xff0c;具体的语法格式如下&#xff1a; def 函…

求二维离散点集的凸包点

1 算法思路 这类求解最外围的点集问题,我们称之为凸包问题,光光是⽤⾁眼去观察的话,这种问题我们很快就能得出答案,并且能马上说出哪⼏个点 是解,但是如果让你敲代码,去解决这类的问题,可能很多⼈会不知道如何去下⼿。 在讲解凸包这类问题的解法前,我们⾸先要先讲下向…

#P1000. [NOIP2008普及组] 立体图

题目描述 小渊是个聪明的孩子&#xff0c;他经常会给周围的小朋友们讲些自己认为有趣的内容。最近&#xff0c;他准备给小朋友们讲解立体图&#xff0c;请你帮他画出立体图。 小渊有一块面积为 m \times nmn 的矩形区域&#xff0c;上面有 m \times nmn 个边长为 11 的格子&a…

Linux环境安装MySQL(详细教程)

1、下载MySQL MySQL官网&#xff1a;MySQLhttps://www.mysql.com/ 下载社区版&#xff08;免费&#xff0c;但不提供技术支持&#xff09; 简单说明一下rpm和tar包的区别&#xff1a; tar 只是一种压缩文件格式&#xff0c;所以&#xff0c;它只是把文件压缩打包 rpm&#xf…

HTML+CSS前端 简易用户登录界面

Day1 刚学了一些html和css的简单语法&#xff0c;尝试写一个非常简易的静态用户登录界面。 login_simple.html <!DOCTYPE html> <html lang"en"><head><meta name"viewport" content"widthdevice-width,initial-scale1.0"…

C++复刻:[流光按钮]+[悬浮波纹按钮]

目录 参考效果实现main.cppdialog.hdialog.cppflowingRayButton.h 流动光线按钮flowingRayButton.cpp 流动光线按钮hoveringRippleButton.h 悬浮波纹按钮hoveringRippleButton.cpp 悬浮波纹按钮模糊知识点 源码 参考 GitHub地址 B站主页 效果 实现 main.cpp #include "…

windows下配置vue开发环境

安装nodejs&#xff0c;配置npm 1.下载安装包&#xff1a;下载地址&#xff1a;https://nodejs.org/en/download 2.安装node&#xff1a;下载完成后进行安装&#xff0c;记住安装的文件夹。本人安装路径为 D:\Program Files\nodejs 3.配置环境变量&#xff1a; ①安装完成后…

高斯滤波和高通滤波

图像在频域里面&#xff0c;频率低的地方说明它是比较平滑的&#xff0c;因为平滑的地方灰度值变化比较小&#xff0c;而频率高的地方通常是边缘或者噪声&#xff0c;因为这些地方往往是灰度值突变的 所谓高通滤波就是保留频率比较高的部分&#xff0c;即突出边缘&#xff1b;…

Node.js介绍;浏览器和Node.j架构区别;Node的安装与管理;JS代码执行方式;Node的输入与输出;全局对象;

目录 1_Node.js介绍1.1_概念1.2_浏览器和Node.j架构区别1.3_Node.js应用场景 2_Node的安装与管理2.1_安装2.2_Node的版本工具2.3_版本管理工具&#xff1a;n 3_JavaScript代码执行4_Node的输入与输出4.1_REPL4.2_Node程序传递参数4.3_Node的输出 5_全局对象5.1_常见的全局对象5…

ClickHouse(六):Clickhouse数据类型-1

进入正文前&#xff0c;感谢宝子们订阅专题、点赞、评论、收藏&#xff01;关注IT贫道&#xff0c;获取高质量博客内容&#xff01; &#x1f3e1;个人主页&#xff1a;含各种IT体系技术&#xff0c;IT贫道_Apache Doris,Kerberos安全认证,大数据OLAP体系技术栈-CSDN博客 &…

一个类似Office用户界面的WPF库

博主介绍&#xff1a; &#x1f308;一个10年开发经验.Net老程序员&#xff0c;微软MVP、博客专家、CSDN/阿里云 .Net领域优质创作者&#xff0c;专注于.Net领域知识、开源项目分享&#xff01;&#x1f308; &#x1f6d5;文末获取&#xff0c;加入交流群&#x1f6d5; &#…

bitset优化例题

1. bitset 优化背包 https://loj.ac/p/515 题意&#xff1a; 给 n 个 < n 的数&#xff0c;每个数有取值范围 a[ i ] - b[ i ]&#xff0c;令 x 为 n 个数的平方和&#xff0c;求能构成的 x 的个数 样例&#xff1a; 5 1 2 2 3 3 4 4 5 5 6 26 思路&#xff1a; 背包d…

VUE之VueRouter页面跳转

参考资料&#xff1a; 参考视频 参考demo及视频资料 VUE之基本部署及VScode常用插件 VUE之基本组成和使用 VUE之Bootstrap和Element-UI的使用 VUE之axios使用&#xff0c;跨域问题&#xff0c;拦截器添加Token Vue Router官网 Vue Router说明&#xff1a; 说明&#xf…

SpringBoot接手JSP项目--【JSB项目实战】

SpringBoot系列文章目录 SpringBoot知识范围-学习步骤【JSB系列之000】 文章目录 SpringBoot系列文章目录[TOC](文章目录) SpringBoot技术很多很多工作之初&#xff0c;面临JSP的老项目我要怎么办环境及工具&#xff1a;项目里可能要用到的技术JSPjstl其它的必要知识 上代码WE…

数据结构:第六章 图

文章目录 一、图的基本概念1.1定义1.2有向图、无向图1.3顶点的度、入度、出度1.4顶点-顶点关系的描述1.5子图和生成子图1.6连通分量1.6强连通分量1.7生成树1.8生成森林1.9边的权、带权图/网1.10几种特殊的图1.11小结 二、图的存储及基本操作2.1邻接矩阵法2.1.1邻接矩阵存储不带…

29_互联网(The Internet)(IP数据包;UDP;TCP;DNS;OSI)

上篇介绍了计算机网络的基础知识&#xff0c;也提到互联网&#xff08;The Internet&#xff09;&#xff0c;本篇将会详细介绍互联网&#xff08;The Internet&#xff09;。 文章目录 1. 互联网&#xff08;The Internet&#xff09;组成及数据包传输过程2. IP 数据包的不足3…

【Spring Boot 源码学习】走近 AutoConfigurationImportSelector

AutoConfigurationImportSelector 源码解析 引言主要内容1. ImportSelector 接口2. DeferredImportSelector 接口3. AutoConfigurationImportSelector 功能概述 总结 引言 上篇博文我们了解了 EnableAutoConfiguration 注解&#xff0c;其中真正实现自动配置功能的核心实现者 …

手把手一起实现Visual Studio 2022本地工程提交(和克隆)Gitee

1、VS2022本地工程提交Gitee 登录Gitee&#xff0c;创建空仓库&#xff0c;如图&#xff1a; 新建仓库&#xff1a; 打开Visual Studio 2022创建的工程&#xff0c;点击创建Git存储库&#xff1a; 复制Gitee仓库URL&#xff1a; 将URL填入&#xff0c;点击创建并推送&#xff…

计算机基本硬件的内部结构

1.早期冯诺依曼机结构 世界上第一台计算机ENIAC是使用手动接线来控制计算&#xff0c;十分麻烦。 冯诺依曼提出“存储程序”的概念&#xff0c;是指将指令以二进制代码的形式事先输入计算机的主存储器&#xff08;内存&#xff09;&#xff0c;然后按照其在存储器中的首地址执…