【数据结构高阶】二叉搜索树

接下来我们来开始使用C++来详细讲解数据结构的一些高阶的知识点

本期讲解的是二叉搜索树,对于初阶二叉树有所遗忘的同学可以看到这里:

【精选】【数据结构初阶】链式二叉树的解析及一些基本操作

讲解二叉搜索树主要是为了后面的map和set做铺垫,废话不多说我们直接上干货:


目录

一、二叉搜索树的概念

二、模拟实现二叉搜索树

2.1 插入数据

2.1.1 插入数据的非递归实现

2.2 遍历数据

2.3 查找数据

2.4 删除数据

2.4.1 删除数据的非递归实现

2.5 模拟实现二叉搜索树的全部代码


一、二叉搜索树的概念

二叉搜索树又称二叉排序树(BST, Binary Search Tree),它可以是一棵空树,或者是具有以下性质的二叉树:

若它的左子树不为空,则左子树上所有节点的值都小于根节点的值

若它的右子树不为空,则右子树上所有节点的值都大于根节点的值

它的左右子树也分别为二叉搜索树

 例如:

 

我们可以发现的一点:无论是什么样的二叉搜索树,使用中序遍历,遍历出的值都是升序排列的

二、模拟实现二叉搜索树

下面又到了我们最激动人心的代码实现环节,本次代码实现我们还是要基于链式二叉树的实现:

template<class K>
struct BSTreeNode//节点
{
	BSTreeNode<K>* _lchild;
	BSTreeNode<K>* _rchild;
	K _key;

	BSTreeNode(const K& key)
		:_lchild(nullptr),
		_rchild(nullptr),
		_key(key)
	{}
};

2.1 插入数据

我们可以根据二叉搜索树的规律来向其中插入数据,但是插入数据时需要注意一点:要记录插入节点的上一个父节点,将插入的节点连接上二叉树:

template<class K>
class BSTree 
{
	typedef BSTreeNode<K> Node;
public:
	bool Insert(const K& key)
	{
		if (_root == nullptr)
		{
			_root = new Node(key);
			_root->_key = key;
			return true;
		}
		Node* cur = _root;//使用cur遍历二叉树找到合适的插入位置
		Node* parent = cur;//记录cur的父节点
		while (cur)
		{
			parent = cur;
			if (key < cur->_key)
			{
				cur = cur->_lchild;
			}
			else if (key > cur->_key)
			{
				cur = cur->_rchild;
			}
			else
			{
				return false;//这里创建的二叉搜索树不允许出现值的冗余
			}
		}
		cur = new Node(key);//创建
		//将创建的节点链接到二叉树上
		if (key < parent->_key)
		{
			parent->_lchild = cur;
		}
		else
		{
			parent->_rchild = cur;
		}
		return true;
	}

private:
	Node* _root = nullptr;//根节点
};

2.1.1 插入数据的非递归实现

递归的效率并没有循环高,那为什么要说一下插入数据的非递归实现呢

主要是非递归的数据插入的传值方法值得一说:

template<class K>
class BSTree 
{
	typedef BSTreeNode<K> Node;
public:
	bool InsertR(const K& key)//插入数据(递归)
	{
		return _InsertR(_root, key);
	}

	bool _InsertR(Node*& root,const K& key)//这里使用指针的引用用来直接修改其父节点指针的指向
	{
		if (root == nullptr)
		{
			root = new Node(key);
			return true;
		}
		if (root->_key > key)
		{
			_InsertR(root->_lchild, key);
		}
		else if (root->_key < key)
		{
			_InsertR(root->_rchild, key);
		}
		else
		{
			return false;
		}
	}
private:
	Node* _root = nullptr;//根节点
};

我们可以看到在递归时,传入的形参类型为Node*&,这样可以直接在其函数内部习惯其父节点孩子指针的指向

那为什么要写两个插入函数呢?因为如果我们直接使用_InsertR函数,无法直接使用对象对_InsertR函数进行传参

2.2 遍历数据

因为二叉搜索树的性质,这里我们采用中序遍历: 

template<class K>
class BSTree 
{
	typedef BSTreeNode<K> Node;
public:
	bool Insert(const K& key)插入数据
	{
        ....
	}

	void InOrder()//中序遍历
	{
		_InOrder(_root);
		cout << endl;
	}

	void _InOrder(Node* root)
	{
		if (root == NULL)//如果是空树就直接结束
		{
			return;
		}
		_InOrder(root->_lchild);//先递归遍历其左子树
		cout << root->_key << " ";//再遍历其根节点
		_InOrder(root->_rchild);//最后递归遍历其右子树
	}

private:
	Node* _root = nullptr;//根节点
};

那为什么要写两个中序遍历函数呢?因为如果我们直接使用_InOrder函数,无法直接使用对象对_InOrder函数进行传参

2.3 查找数据

template<class K>
class BSTree 
{
	typedef BSTreeNode<K> Node;
public:
	bool Insert(const K& key)//插入数据
	{
        ......
	}

	void InOrder()//中序遍历
	{
        ......
	}

	void _InOrder(Node* root)
	{
        ......
	}

	bool Find(const K& key)//查找数据
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				cur = cur->_rchild;
			}
			else if (cur->_key > key)
			{
				cur = cur->_lchild;
			}
			else
			{
				return true;
			}
		}
		return false;
	}

private:
	Node* _root = nullptr;//根节点
};

2.4 删除数据

对于在二叉搜索树中删除数据,我们就要好好说道说道了

下面我们有这样的一个二叉搜索树:

现在我们要删除其叶子节点,这很容易,直接删除完,再将其父节点对应的孩子指针置空即可

那我们要删只有一个孩子节点的数据呢?例如14和10

这个稍微麻烦一点,删除完该节点后将其孩子节点托付给其父节点即可:

那要删带有两个孩子节点的数据呢?例如3、6、8:

对于这种情况我们可以选择其节点下的左子树的最大节点(左子树的最右节点),或者右子树的最小节点(右子树的最左节点)来替代要删除的节点:

综上所述,要删除的结点可能分下面四种情况:

a. 要删除的结点无孩子结点

b. 要删除的结点只有左孩子结点 

c. 要删除的结点只有右孩子结点

d. 要删除的结点有左、右孩子结点

看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程如下:

情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点--直接删除

情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点--直接删除

情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题--替换法删除

思路我们有了,下面用代码来实现一下:

template<class K>
class BSTree 
{
	typedef BSTreeNode<K> Node;
public:
	bool Insert(const K& key)//插入数据
	{
        ...
	}

	void InOrder()//中序遍历
	{
        ...
	}

private:
	void _InOrder(Node* root)
	{
        ...
	}

public:
	bool Find(const K& key)//查找数据
	{
        ...
	}

	bool Erase(const K& key)
	{
		Node* cur = _root;//使用cur遍历二叉树找到要删除元素的位置
		Node* parent = cur;//记录cur的父节点
		while (cur)//寻找需要删除的节点
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_rchild;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_lchild;
			}
			else//找到了,开始删除
			{
				if (cur->_lchild == cur->_rchild && cur->_lchild == nullptr)//删除的节点为叶子节点
				{
					if (parent == cur)//删除的是根节点
					{
						_root = nullptr;//更新根节点
					}
					//将其父节点指向的自身的指针置空
					else if (parent->_lchild == cur)
					{
						parent->_lchild = nullptr;
					}
					else
					{
						parent->_rchild = nullptr;
					}
					//释放节点空间
					delete cur;
					cur = nullptr;
				}
				else if (cur->_rchild == nullptr)//删除的节点右孩子为空,左孩子不为空
				{
					if (parent == cur)//删除的是根节点
					{
						_root = cur->_lchild;//更新根节点
					}
					//将删除节点的左孩子交给其父节点
					else if (parent->_lchild == cur)
					{
						parent->_lchild = cur->_lchild;
					}
					else
					{
						parent->_rchild = cur->_lchild;
					}
					//释放节点空间
					delete cur;
					cur = nullptr;
				}
				else if (cur->_lchild == nullptr)//删除的节点左孩子为空,右孩子不为空
				{
					if (parent == cur)//删除的是根节点
					{
						_root = cur->_rchild;//更新根节点
					}
					//将删除节点的右孩子交给其父节点
					else if (parent->_lchild == cur)
					{
						parent->_lchild = cur->_rchild;
					}
					else
					{
						parent->_rchild = cur->_rchild;
					}
					//释放节点空间
					delete cur;
					cur = nullptr;
				}
				else//删除的节点左右孩子都不为空,要找到删除节点的左子树的最大节点或右子树的最小节点可将其替换
				{
					//这里找要删除节点的左子树的最大节点(右子树的最小节点也可)
					Node* maxleft = cur->_lchild; // 记录左子树的最大节点
					Node* pmaxleft = cur;//记录左子树的最大节点的父节点
					while (maxleft->_rchild)//查找左子树的最大节点
					{
						pmaxleft = maxleft;
						maxleft = maxleft->_rchild;
					}
					//将找到的节点替换要删除的节点
					cur->_key = maxleft->_key;
					if (pmaxleft->_lchild == maxleft)
					{
						pmaxleft->_lchild = maxleft->_lchild;
					}
					else
					{
						pmaxleft->_rchild = maxleft->_lchild;
					}
					//释放节点空间
					delete maxleft;
					maxleft = nullptr;
				}
				return true;
			}
		}
		return false;//没找到要删除的节点
	}
private:
	Node* _root = nullptr;//根节点
};

2.4.1 删除数据的非递归实现

template<class K>
class BSTree 
{
	typedef BSTreeNode<K> Node;
public:
	bool EraseR(const K& key)//递归删除数据
	{
		return _EraseR(_root, key);
	}

	bool _EraseR(Node*& root, const K& key)
	{
		if (root == nullptr)
		{
			return false;
		}
		else if (root->_key < key)
		{
			_EraseR(root->_rchild, key);
		}
		else if (root->_key > key)
		{
			_EraseR(root->_lchild, key);
		}
		else
		{
			Node* del = root;
			if (root->_lchild == root->_rchild && root->_lchild == nullptr)//删除的节点为叶子节点
			{
				//释放节点空间
				delete root;
				//将其父节点指向的自身的指针置空
				root = nullptr;
			}
			else if (root->_rchild == nullptr)//删除的节点右孩子为空,左孩子不为空
			{
				//将删除节点的左孩子交给其父节点
				root = root->_lchild;
				//释放节点空间
				delete del;
				del = nullptr;
			}
			else if (root->_lchild == nullptr)//删除的节点左孩子为空,右孩子不为空
			{
				//将删除节点的右孩子交给其父节点
				root = root->_rchild;
				//释放节点空间
				delete del;
				del = nullptr;
			}
			else//删除的节点左右孩子都不为空,要找到删除节点的左子树的最大节点或右子树的最小节点将其替换
			{
				//这里找要删除节点的右子树的最小节点(左子树的最大节点也可)
				Node* minright = del->_rchild; // 记录右子树的最小节点
				while (minright->_lchild)//查找右子树的最小节点
				{
					minright = minright->_lchild;
				}
				root->_key = minright->_key;//将找到的节点替换要删除的节点
				return _EraseR(root->_rchild, root->_key);//继续递归删除其右子树中用来替换的节点
			}
			return true;
		}
	}

private:
	Node* _root = nullptr;//根节点
};

 

2.5 模拟实现二叉搜索树的全部代码

#include<iostream>
using namespace std;

template<class K>
struct BSTreeNode//节点
{
	BSTreeNode<K>* _lchild;
	BSTreeNode<K>* _rchild;
	K _key;

	BSTreeNode(const K& key)
		:_lchild(nullptr),
		_rchild(nullptr),
		_key(key)
	{}
};

template<class K>
class BSTree 
{
	typedef BSTreeNode<K> Node;
public:
	bool Insert(const K& key)//插入数据
	{
		if (_root == nullptr)//如果根节点为空就直接插入
		{
			_root = new Node(key);
			_root->_key = key;
			return true;
		}
		Node* cur = _root;//使用cur遍历二叉树找到合适的插入位置
		Node* parent = cur;//记录cur的父节点
		while (cur)
		{
			parent = cur;
			if (key < cur->_key)
			{
				cur = cur->_lchild;
			}
			else if (key > cur->_key)
			{
				cur = cur->_rchild;
			}
			else
			{
				return false;//这里创建的二叉搜索树不允许出现值的冗余
			}
		}
		cur = new Node(key);//创建
		//将创建的节点链接到二叉树上
		if (key < parent->_key)
		{
			parent->_lchild = cur;
		}
		else
		{
			parent->_rchild = cur;
		}
		return true;
	}

	bool InsertR(const K& key)//插入数据(递归)
	{
		return _InsertR(_root, key);
	}

private:
	bool _InsertR(Node*& root,const K& key)//这里使用指针的引用用来直接修改其父节点指针的指向
	{
		if (root == nullptr)
		{
			root = new Node(key);
			return true;
		}
		if (root->_key > key)
		{
			_InsertR(root->_lchild, key);
		}
		else if (root->_key < key)
		{
			_InsertR(root->_rchild, key);
		}
		else
		{
			return false;
		}
	}

public:
	void InOrder()//中序遍历
	{
		_InOrder(_root);
		cout << endl;
	}

private:
	void _InOrder(Node* root)
	{
		if (root == NULL)//如果是空树就直接结束
		{
			return;
		}
		_InOrder(root->_lchild);//先递归遍历其左子树
		cout << root->_key << " ";//再遍历其根节点
		_InOrder(root->_rchild);//最后递归遍历其右子树
	}

public:
	bool Find(const K& key)//查找数据
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				cur = cur->_rchild;
			}
			else if (cur->_key > key)
			{
				cur = cur->_lchild;
			}
			else
			{
				return true;
			}
		}
		return false;
	}

	bool Erase(const K& key)//删除数据
	{
		Node* cur = _root;//使用cur遍历二叉树找到要删除元素的位置
		Node* parent = cur;//记录cur的父节点
		while (cur)//寻找需要删除的节点
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_rchild;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_lchild;
			}
			else//找到了,开始删除
			{
				if (cur->_lchild == cur->_rchild && cur->_lchild == nullptr)//删除的节点为叶子节点
				{
					if (parent == cur)//删除的是根节点
					{
						_root = nullptr;//更新根节点
					}
					//将其父节点指向的自身的指针置空
					else if (parent->_lchild == cur)
					{
						parent->_lchild = nullptr;
					}
					else
					{
						parent->_rchild = nullptr;
					}
					//释放节点空间
					delete cur;
					cur = nullptr;
				}
				else if (cur->_rchild == nullptr)//删除的节点右孩子为空,左孩子不为空
				{
					if (parent == cur)//删除的是根节点
					{
						_root = cur->_lchild;//更新根节点
					}
					//将删除节点的左孩子交给其父节点
					else if (parent->_lchild == cur)
					{
						parent->_lchild = cur->_lchild;
					}
					else
					{
						parent->_rchild = cur->_lchild;
					}
					//释放节点空间
					delete cur;
					cur = nullptr;
				}
				else if (cur->_lchild == nullptr)//删除的节点左孩子为空,右孩子不为空
				{
					if (parent == cur)//删除的是根节点
					{
						_root = cur->_rchild;//更新根节点
					}
					//将删除节点的右孩子交给其父节点
					else if (parent->_lchild == cur)
					{
						parent->_lchild = cur->_rchild;
					}
					else
					{
						parent->_rchild = cur->_rchild;
					}
					//释放节点空间
					delete cur;
					cur = nullptr;
				}
				else//删除的节点左右孩子都不为空,要找到删除节点的左子树的最大节点或右子树的最小节点可将其替换
				{
					//这里找要删除节点的左子树的最大节点(右子树的最小节点也可)
					Node* maxleft = cur->_lchild; // 记录左子树的最大节点
					Node* pmaxleft = cur;//记录左子树的最大节点的父节点
					while (maxleft->_rchild)//查找左子树的最大节点
					{
						pmaxleft = maxleft;
						maxleft = maxleft->_rchild;
					}
					//将找到的节点替换要删除的节点
					cur->_key = maxleft->_key;
					if (pmaxleft->_lchild == maxleft)
					{
						pmaxleft->_lchild = maxleft->_lchild;
					}
					else
					{
						pmaxleft->_rchild = maxleft->_lchild;
					}
					//释放节点空间
					delete maxleft;
					maxleft = nullptr;
				}
				return true;
			}
		}
		return false;//没找到要删除的节点
	}


	bool EraseR(const K& key)//递归删除数据
	{
		return _EraseR(_root, key);
	}

private:
	bool _EraseR(Node*& root, const K& key)
	{
		if (root == nullptr)
		{
			return false;
		}
		else if (root->_key < key)
		{
			_EraseR(root->_rchild, key);
		}
		else if (root->_key > key)
		{
			_EraseR(root->_lchild, key);
		}
		else
		{
			Node* del = root;
			if (root->_lchild == root->_rchild && root->_lchild == nullptr)//删除的节点为叶子节点
			{
				//释放节点空间
				delete root;
				//将其父节点指向的自身的指针置空
				root = nullptr;
			}
			else if (root->_rchild == nullptr)//删除的节点右孩子为空,左孩子不为空
			{
				//将删除节点的左孩子交给其父节点
				root = root->_lchild;
				//释放节点空间
				delete del;
				del = nullptr;
			}
			else if (root->_lchild == nullptr)//删除的节点左孩子为空,右孩子不为空
			{
				//将删除节点的右孩子交给其父节点
				root = root->_rchild;
				//释放节点空间
				delete del;
				del = nullptr;
			}
			else//删除的节点左右孩子都不为空,要找到删除节点的左子树的最大节点或右子树的最小节点将其替换
			{
				//这里找要删除节点的右子树的最小节点(左子树的最大节点也可)
				Node* minright = del->_rchild; // 记录右子树的最小节点
				while (minright->_lchild)//查找右子树的最小节点
				{
					minright = minright->_lchild;
				}
				root->_key = minright->_key;//将找到的节点替换要删除的节点
				return _EraseR(root->_rchild, root->_key);//继续递归删除其右子树中用来替换的节点
			}
			return true;
		}
	}

private:
	Node* _root = nullptr;//根节点
};

本期博客到这里就结束了,代码量较大,还请各位仔细分析呀

我们下期见~

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

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

相关文章

HTML易忽略的角落【目录】

目前已有文章 **** 篇 本专栏是汇集了一些HTML常常被遗忘的知识&#xff0c;这里算是温故而知新&#xff0c;往往这些零碎的知识点&#xff0c;在你开发中能起到炸惊效果。我们每个人都没有过目不忘&#xff0c;过久不忘的本事&#xff0c;就让这一点点知识慢慢渗透你的脑海。 …

【Spring】超详细讲解AOP(面向切面编程)

文章目录 1. 前言2. 什么是AOP3. AOP快速入门4. AOP的核心概念5. 切点表达式6. 切点函数7. 通知8. 总结 1. 前言 本文围绕AOP进行讲解,AOP可以做什么,涉及到了哪些注解,以及各个注解运行的时机,以及Around相较于其它注解有什么不同,并且如果要执行目标方法需要怎么做 2. 什么…

群晖7.2版本通过Container Manager安装xiaoya-alist

小雅Alist&#xff0c;可以说是Alist应用中挂载阿里云最完美的成功案例。 一、下载镜像 注册表中下载镜像 Container Manager应该是7.2版本才改名&#xff0c;就是以前的docker。 打开【Container Manager】-【注册表】-【搜索框】搜索 xiaoya 内容区域&#xff0c;搜出的…

新零售系统平台解决方案 线上线下小程序怎么做

新零售线上线下解决方案是将传统零售业务与互联网科技相结合&#xff0c;通过数字化、智能化手段提升零售业务效率和用户体验的解决方案&#xff0c;它既有提供消费者线下体验&#xff0c;强调“稳”&#xff0c;又有互联网线上的“快”。 线上线下小程序可以通过一体化的进销存…

ubuntu20.04安装cv2

查看ubuntu的版本 cat /etc/lsb-release DISTRIB_IDUbuntu DISTRIB_RELEASE20.04 DISTRIB_CODENAMEfocal DISTRIB_DESCRIPTION"Ubuntu 20.04.3 LTS"更改镜像源 cp /etc/apt/sources.list /etc/apt/sources.list.bak cat > /etc/apt/sources.listdeb http://mirr…

第二证券:注册制退市规则?

跟着我国本钱商场不断发展和完善&#xff0c;持续注重退市原则改造也成为了商场中的热点话题。而注册制退市规矩的施行&#xff0c;无疑是新的退市原则下的一大重要内容。 首要&#xff0c;咱们需求了解什么是注册制退市规矩。所谓注册制退市规矩&#xff0c;指的是在注册制下…

App加固中的代码混淆功能,让逆向工程师很头疼

App加固中的代码混淆功能&#xff0c;让逆向工程师很头疼 “我想离开浪浪山。” 在数次尝试破解某个App 时&#xff0c;某个逆向工程师无奈感慨道。 逆向工程师顾名思义就是把一个个完整的软件逆推&#xff0c;还原成一段段代码&#xff0c;方便破解。 比如给他们一个手机Ap…

windows 使用WinSW制作服务

背景&#xff1a;最近维护老项目&#xff0c;需要使用windows server 2012 r2部署项目。使用springboot开发项目&#xff0c;nginx部署前端&#xff0c;于是打算把jar包和nginx都制作成服务 下载winsw地址&#xff1a;https://github.com/winsw/winsw/releases 下载这两个文件…

内衣迷你洗衣机什么牌子好?选购内衣裤洗衣机的方法

洗衣机在我们的生活中可谓是非常常见的了&#xff0c;几乎每家每户都具备着一台。即便是有洗衣机&#xff0c;也有不少人不会将自己我贴身衣物直接扔在洗衣机里清洗&#xff0c;而是会自己手工手洗。这跟我们传统上的观念有很大的关系&#xff0c;认为把内衣、内裤等贴身衣物放…

【23真题】发错试卷?想多了,只是题型大改!

今天分享的是23年南昌大学811的信号与系统试题及解析。南昌大学23年题型大改&#xff0c;加入了很多电路题目&#xff01;23考研的同学&#xff0c;甚至考场上以为发错试卷&#xff0c;考的电路原理。所以学有余力的同学&#xff0c;一定跟着我做各种院校的真题&#xff0c;见多…

华为 Mate 60 Pro 拆解:陆制零件比率上升至47% | 百能云芯

近日&#xff0c;日经新闻联合研究公司Fomalhaut Techno Solutions对华为 Mate 60 Pro 进行了拆解&#xff0c;揭示了这款于8月发布的新型智能手机的成本结构。拆解结果显示&#xff0c;该手机的国产零部件比例达到了47%&#xff0c;相较于三年前的 Mate 40 Pro&#xff0c;提高…

Js:获取最近6个月的月份(包含本月、不包含本月)

一、需求 获取最近6个月的月份&#xff08;不包含本月&#xff09;&#xff0c;比如现在是11月份&#xff0c;则需要获取到的月份是&#xff1a;10、9、8、7、6、5将月份从小到大排列 二、解决 1、获取最近的6个月份&#xff08;不包含本月&#xff09; var monthALL[]; …

欧盟铅镉RSL邻苯项目化学物质检测报告办理(RSL Report 资质)REACH 认证

如果您在亚马逊上销售商品&#xff0c;则必须遵守所有适用的欧盟和地方法律法规&#xff0c;以及适用于这些商品和商品信息的亚马逊政策。要在亚马逊上销售某些商品&#xff0c;)您需要向我们提供 REACH 符合性声明或检测报告。 RSL-Phthalate资质 欧盟RSL邻苯项目检测报告 Ph…

JVM jstat 查看内存新生代老年代回收情况,排查oom

jstat 命令 jstat - [-t] [-h] [ []] option&#xff1a;我们经常使用的选项有gc、gcutil vmid&#xff1a;java进程id interval&#xff1a;间隔时间&#xff0c;单位为毫秒 count&#xff1a;打印次数 每秒打印一次 jstat -gc 9162 1000S0C:年轻代第一个survivor的容量…

Kubernetes(k8s)介绍和环境部署

文章目录 Kubernetes一、Kubernetes介绍1.Kubernetes简介2.Kubernetes概念3.Kubernetes功能4.Kubernetes工作原理5.kubernetes组件6.Kubernetes优缺点 二、Kubernetes环境部署环境基本配置1.所有节点安装docker2.所有节点安装kubeadm、kubelet、kubectl添加yum源containerd配置…

用照片预测人的年龄【图像回归】

在图像分类任务中&#xff0c;卷积神经网络 (CNN) 是非常强大的神经网络架构。 然而&#xff0c;鲜为人知的是&#xff0c;它们同样能够执行图像回归任务。 图像分类和图像回归任务之间的基本区别在于分类任务中的目标变量&#xff08;我们试图预测的东西&#xff09;不是连续…

图片转excel表格怎么弄?有何密笈?

一般的软件要将图片转excel表格&#xff0c;都需要待识别的图片要有明显清晰的表格线&#xff0c;但金鸣识别则不需要这些条件的限制&#xff0c;即便是无表格线或缺少横线或竖线的图片&#xff0c;也能很好地识别成excel&#xff0c;另外&#xff0c;别的软件一般会限制文件大…

Windows Server 2012 R2系统服务器远程桌面服务多用户登录配置分享

Windows Server 2012系统在没有安装远程多界面的情况下&#xff0c;最多只能同时运行2个远程桌面&#xff0c;如果是有多个技术员、合伙人同时操作或是像游戏开发需要用到多界面&#xff0c;但是没有安装就很不方便&#xff0c;今天飞飞来和你们分享Windows server 2012R2系统远…

Python武器库开发-flask篇之模板渲染(二十四)

flask篇之模板渲染(二十四) Flask 中的模板是一种将数据和 HTML 代码组合在一起的方式&#xff0c;使得我们可以生成动态的 HTML 页面。使用模板可以使我们的代码更加简洁、易于维护和复用。在真实的环境中&#xff0c;我们往往接触到的是由 html、CSS和JavaScript所做的网页&…

51单片机应用从零开始(四)

51单片机应用从零开始&#xff08;一&#xff09;-CSDN博客 51单片机应用从零开始&#xff08;二&#xff09;-CSDN博客 51单片机应用从零开始&#xff08;三&#xff09;-CSDN博客 详解 KEIL C51 软件的使用建立工程-CSDN博客 详解 KEIL C51 软件的使用设置工程编绎与连接程序…