【STL】平衡二叉树

news2025/7/14 2:16:42

前言

对于之前普通的二叉搜索树,其搜索的效率依靠树的形状来决定,如下:

d6b39cc3ecbe4318a5bbd780ecefb9b3.png

可以看到 A图 中的树比较彭亨,搜索一个元素的效率接近 O(logN) ;而 B图 中的形状也符合搜索二叉树,但是很不平衡,这时的搜索效率就变成 O(N) 了(如搜索值为4的节点)

可以看到,如果搜索二叉树不平衡,他的搜索的效率不确定,在 O(logN) 和 O(N) 之间,可能有暴击;

那么对此,既然不平衡,那我们就给出平衡的两种方法:

1.构成AVL树

2.构成红黑树

这两种方法都是为了平衡搜索二叉树而生

AVL树

1. AVL树的概念和性质

它的出现是为了 搜索二叉树的平衡,那就来了解AVL树的概念和性质,也就是什么是AVL,并且作为一颗AVL树的要求是什么

首先AVL树是一颗树,它要求任何一个节点的左右子树的高度差不超过一,以此来保证平衡,是AVL树的核心

来看看它的做法

与搜索二叉树不同,它将二叉链,变成了三叉链(多了指向父节点的指针),并且引入了平衡因子这个东西;这些都是为了方便并且为了树的一个平衡引入

三叉链和平衡因子和下面AVL树属性的图结合起来看更直观

三叉链也就是多了一个 parent指针 指向该节点的父节点,这一操作可以让节点找到其祖先

平衡因子是指该节点的 右子树高度 减去 左子树高度的值,而根据AVL树的性质(要求任何一个节点的左右子树的高度差不超过一),也就是平衡因子 _bf 的值域为 { -1, 0, 1 } ,如果平衡因子 _bf 的值出现为 2 或者 -2 ,说明该树已经不平衡,此时需要进行停止调整,先进行旋转操作

2. AVL树类的属性

这里的节点值类型简化为了int类型,原为 pair

8d3c11a09a094a64b8137292a659eedc.png

bd509a65fd8946ffbb38ca7fb689e048.png

3. AVL树的插入函数

而AVL树的重点呢,也就是AVL树是如何让树达到平衡的呢?就是每插入了一个节点后,进行了一些调整,如果插入节点后不平衡,那么会将其调整成平衡

插入后对插入节点祖先的平衡因子进行调整,当调整过程中,有平衡因子出现异常时,此时通过旋转的方式,让树继续保持平衡

当平衡因子出现异常即是 平衡因子的值更新为 2 或者 -2 时,说明树现在已经不平衡,需要进行旋转

分情况讨论:

a6f7bbde65044588ad1a8e0c6d46229b.png

现在平衡因子已经出现了不平衡,为什么旋转之后,树就可以继续平衡呢?

具体看到旋转 (这里以左单旋为例):

定义节点指针 parent(图中的节点值为30), cur(图中的60)

左单旋是将 cur 的左孩子给给 parent 的右, 再把 parent 赋值给 cur 的左,旋转完成

再进行平衡因子的更新:parent 和 cur 的平衡因子 _bf 都变成 0

32e08c9f357a4f2db020b7bdfb4e58a1.png

左单旋具体代码如下:

        void RotateL(Node* parent)
		{
			assert(parent);

			Node* cur = parent->_right;
			Node* cur_left = cur->_left;

			// 改变节点之间的关系

			parent->_right = cur_left;
			cur->_left = parent;

			
			// 改变_parent的指向

			//原本 parent 的 _parent
			Node* ppNode = parent->_parent;

			//若原本 ppNode(原本 parent 的 _parent)为空,说明parent原本不是根,现需把             ppNode 给到 cur 的 _parent,并且把 ppNode 的子更新为 cur
			if (ppNode)
			{
				if (ppNode->_left == parent)
				{
					ppNode->_left = cur;
				}
				else
				{
					ppNode->_right = cur;
				}

				cur->_parent = ppNode;
			}
			//若原本 parent->_parent 为空,说明parent原本是根,现需把_root更新为根
			else
			{
				_root = cur;
				cur->_parent = nullptr;
			}

			if (cur_left)
			{
				cur_left->_parent = parent;
			}

			parent->_parent = cur;


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

对左单旋的总结是:当不平衡的树进行左单旋之后,树又被调整为平衡,然后再进行平衡因子的更新

那么右单旋的思路是一样的,自己可以进行推理

而要进行双旋时候,可以再进行讨论(这里以右左旋进行讨论):

8cd424f049604210a6c94074d65f57a3.png

右左旋:定义 parent(下图节点值为30), cur(下图节点值为90), cur_left(下图节点值为60)

双旋都是两大步,这里右左旋就是先进行右旋,再进行左旋,旋转结束;对,这里对刚刚实现的单旋代码进行了复用,复用yyds

具体是对 cur 先进行右旋,再对 parent 进行左旋,如下

dcb5820617d4473c95c273a54add59f1.png

最后进行平衡因子的调整,但是这里特别容易忘记第三种情况的讨论,AVL树的这个细节得注意:

188fbf8bb0974858b161474841cdfb82.png

右左旋的代码如下:

        void RotateRL(Node* parent)
		{
			Node* cur = parent->_right;
			Node* cur_left = cur->_left;
			int bf = cur_left->_bf;

			RotateR(parent->_right);
			RotateL(parent);

			parent->_bf = 0;
			cur->_bf = 0;
			cur_left->_bf = 0;

			if (bf == -1)
			{
				cur->_bf = 1;
			}
			if (bf == 1)
			{
				parent->_bf = -1;
			}
		
		}

那么左右旋的思路是一样的,自己可以进行推理

4. 总结

而进行何种旋转本质是取决于 parent cur 和 cur_left (或cur_right)之间的关系,若他们三个所连成的是直线,那么进行单旋操作,如果为折线,那就进行双旋操作;而这里的平衡因子刚好可以体现他们之间的关系,所以上图中根据平衡因子来决定单双旋和左右旋转

AVL树构造和插入函数,测试函数:

这里的节点值类型简化为了int类型,原为 pair

#pragma once

#include <iostream>

using namespace std;

#include <string>

#include <array>

#include <deque>

#include <list>

#include <map>

#include <queue>

#include <set>

#include <stack>

#include <unordered_map>

#include <unordered_set>

#include <vector>


#include <algorithm>


#include <assert.h>

#include <windows.h>

#include <ctime>




namespace zhuandrong
{

	class AVLTreeNode
	{
		typedef AVLTreeNode Node;

	public:

		int _val;

		Node* _left;
		Node* _right;
		Node* _parent;
		
		int _bf;

	public:

		AVLTreeNode(const int val = 0)
			: _val(val)
			, _left(nullptr)
			, _right(nullptr)
			, _parent(nullptr)
			, _bf(0)
		{}
	};


	class AVLTree
	{
		typedef AVLTreeNode Node;

	protected:
		
		void RotateL(Node* parent)
		{
			assert(parent);

			Node* cur = parent->_right;
			Node* cur_left = cur->_left;

			// 改变节点之间的关系

			parent->_right = cur_left;
			cur->_left = parent;

			
			// 改变_parent的指向

			//原本 parent 的 _parent
			Node* ppNode = parent->_parent;

			//若原本 ppNode(原本 parent 的 _parent)为空,说明parent原本不是根,现需把 ppNode 给到 cur 的 _parent,并且把 ppNode 的子更新为 cur
			if (ppNode)
			{
				if (ppNode->_left == parent)
				{
					ppNode->_left = cur;
				}
				else
				{
					ppNode->_right = cur;
				}

				cur->_parent = ppNode;
			}
			//若原本 parent->_parent 为空,说明parent原本是根,现需把_root更新为根
			else
			{
				_root = cur;
				cur->_parent = nullptr;
			}

			if (cur_left)
			{
				cur_left->_parent = parent;
			}

			parent->_parent = cur;


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


		//void RotateL(Node* parent)
		//{
		//	Node* cur = parent->_right;
		//	Node* cur_left = cur->_left;

		//	parent->_right = cur_left;
		//	cur->_left = parent;

		//	Node* ppNode = parent->_parent;
		//	if (cur_left)//右孩子可能存在,也可能不存在,所以需要判断,需要在parent改变前判断
		//	{
		//		cur_left->_parent = parent;
		//	}

		//	parent->_parent = cur;

		//	if (parent == _root)//parent可能是根节点,也可能不是根节点
		//	{
		//		_root = cur;
		//		cur->_parent = nullptr;
		//	}
		//	else
		//	{
		//		if (ppNode->_left == parent)
		//		{
		//			ppNode->_left = cur;
		//		}
		//		else
		//		{
		//			ppNode->_right = cur;
		//		}
		//		cur->_parent = ppNode;
		//	}
		//	cur->_bf = parent->_bf = 0;//将平衡因子调整
		//}


		void RotateR(Node* parent)
		{
			assert(parent);

			Node* cur = parent->_left;
			Node* cur_right = cur->_right;

			// 改变节点之间的关系

			parent->_left = cur_right;
			cur->_right = parent;


			// 改变_parent的指向

			//原本 parent 的 _parent
			Node* ppNode = parent->_parent;

			//若原本 ppNode(原本 parent 的 _parent)为空,说明parent原本不是根,现需把 ppNode 给到 cur 的 _parent,并且把 ppNode 的子更新为 cur
			if (ppNode)
			{
				if (ppNode->_left == parent)
				{
					ppNode->_left = cur;
				}
				else
				{
					ppNode->_right = cur;
				}

				cur->_parent = ppNode;
			}
			//若原本 parent->_parent 为空,说明parent原本是根,现需把_root更新为根
			else
			{
				_root = cur;
				cur->_parent = nullptr;
			}

			if (cur_right)
			{
				cur_right->_parent = parent;
			}

			parent->_parent = cur;


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

		}


		//void RotateR(Node* parent)
		//{
		//	Node* cur = parent->_left;
		//	Node* curRight = cur->_right;

		//	parent->_left = curRight;
		//	cur->_right = parent;
		//	Node* ppNode = parent->_parent;
		//	if (curRight)//右孩子可能存在,也可能不存在,所以需要判断,需要在parent改变前判断
		//	{
		//		curRight->_parent = parent;
		//	}

		//	parent->_parent = cur;

		//	if (parent == _root)//parent可能是根节点,也可能不是根节点
		//	{
		//		_root = cur;
		//		cur->_parent = nullptr;
		//	}
		//	else
		//	{
		//		if (ppNode->_left == parent)
		//		{
		//			ppNode->_left = cur;
		//		}
		//		else
		//		{
		//			ppNode->_right = cur;
		//		}
		//		cur->_parent = ppNode;
		//	}
		//	cur->_bf = parent->_bf = 0;//将平衡因子调整
		//}
		

		void RotateRL(Node* parent)
		{
			Node* cur = parent->_right;
			Node* cur_left = cur->_left;
			int bf = cur_left->_bf;

			RotateR(parent->_right);
			RotateL(parent);

			/*parent->_bf = cur_left->_bf = 0;
			cur->_bf = flag;*/

			parent->_bf = 0;
			cur->_bf = 0;
			cur_left->_bf = 0;

			if (bf == -1)
			{
				cur->_bf = 1;
			}
			if (bf == 1)
			{
				parent->_bf = -1;
			}
		
		}


		void RotateLR(Node* parent)
		{
			Node* cur = parent->_left;
			Node* cur_right = cur->_right;
			int bf = cur_right->_bf;

			RotateL(parent->_left);
			RotateR(parent);

			/*parent->_bf = parent->_parent->_bf = 0;
			parent->_parent->_left->_bf = -1;*/
			
			cur->_bf = 0;
			parent->_bf = 0;
			cur_right->_bf = 0;

			if (bf == 1)
			{
				cur->_bf = -1;
			}
			if (bf == -1)
			{
				parent->_bf = 1;
			}

		}


	protected:
		Node* _root;

	public:
		AVLTree()
			: _root(nullptr)
		{}

		AVLTree(vector<int> v)
		{
			for (auto& e : v)
			{
				if (e == 8)
				{
					int i = 0;
				}

				insert(e);

				assert(IsBalance());
			}
		}

		bool insert(int val)
		{
			// 判断是否有根节点
			if (_root)
			{
				//先找到合适的插入位置
				Node* cur = _root;
				Node* parent = nullptr;

				while (cur)
				{
					if (val < cur->_val)
					{
						parent = cur;
						cur = cur->_left;
					}
					else if (val > cur->_val)
					{
						parent = cur;
						cur = cur->_right;
					}
					else
					{
						cout << "插入的元素值已重复" << endl;
						return false;
					}
				}

				cur = new Node(val);
				cur->_parent = parent;

				if (val < parent->_val)
				{
					parent->_left = cur;
				}
				else
				{
					parent->_right = cur;
				}

				//对平衡因子进行更新

				while (parent)
				{
					if (cur == parent->_left)
					{
						--parent->_bf;
					}
					else if (cur == parent->_right)
					{
						++parent->_bf;
					}

					//若 parent->_bf == 0,表明平衡因子调整结束
					if (!parent->_bf)
					{
						break;
					}
					else if (parent->_bf == 1 || parent->_bf == -1)
					{
						cur = parent;
						parent = parent->_parent;
					}
					//若parent->_bf == 2 && cur->_bf == 1,将parent进行左单旋
					else if (parent->_bf == 2 && cur->_bf == 1)
					{
						RotateL(parent);
						break;
					}
					//若parent->_bf == 2 && cur->_bf == -1,将parent进行右左单旋
					else if (parent->_bf == 2 && cur->_bf == -1)
					{
						RotateRL(parent);
						break;
					}
					//若parent->_bf == -2 && cur->_bf == -1,将parent进行右单旋
					else if (parent->_bf == -2 && cur->_bf == -1)
					{
						RotateR(parent);
						break;
					}
					//若parent->_bf == -2 && cur->_bf == 1,将parent进行左右单旋
					else if (parent->_bf == -2 && cur->_bf == 1)
					{
						RotateLR(parent);
						break;
					}
					else
					{
						assert(false);
					}

				}

			}
			else
			{
				_root = new Node(val);
			}

			return true;
		}

		int TreeHight(Node* root)
		{
			if (root == nullptr)
				return 0;
			int leftHight = TreeHight(root->_left);
			int rightHight = TreeHight(root->_right);
			return leftHight > rightHight ? leftHight + 1 : rightHight + 1;
		}

		void Inorder()
		{
			_Inorder(_root);
		}

		bool IsBalance()
		{
			return _IsBalance(_root);
		}

	private:

		void _Inorder(Node* root)
		{
			if (root == nullptr)
				return;

			_Inorder(root->_left);
			cout << root->_val << endl;
			_Inorder(root->_right);
		}


		bool _IsBalance(Node* root)
		{
			if (root == nullptr)
				return true;
			int leftHight = TreeHight(root->_left);
			int rightHight = TreeHight(root->_right);
			//检查平衡因子对不对
			if (rightHight - leftHight != root->_bf)
			{
				cout << "平衡因子出现异常" << endl;

				cout << rightHight - leftHight << endl;

				return false;
			}
			//需要递归检查是否平衡
			return (leftHight - rightHight <= 1 && leftHight - rightHight >= -1)
				&& _IsBalance(root->_left) && _IsBalance(root->_right);
		}

	};


	void test_AVLTree()
	{
		/*vector<int> v = { 6, 4, 32, 2, 7, 8 };

		AVLTree avl_tree(v);
		assert(avl_tree.IsBalance());*/


		AVLTree avl_tree;

		srand((unsigned int)time(NULL));

		for (int i = 1000; i >= 0; --i)
		{
			avl_tree.insert(rand());
			//avl_tree.insert(i);
			assert(avl_tree.IsBalance());
		}


		//srand((unsigned int)time(0));
		//const size_t N = 10000;
		//for (size_t i = 0; i < N; ++i)
		//{
		//	size_t x = rand();
		//	avl_tree.insert(x);
		//	//cout << t.IsBalance() << endl;
		//}

		//avl_tree.Inorder();

		//cout << avl_tree.IsBalance() << endl;


	}


}

红黑树

1. 红黑树的概念和性质(什么是红黑树,并且作为一颗红黑树的要求)

8f14b4f1c0434c04abce9efca47479a1.png

以上是红黑树的概念和性质,在红黑树里不仅仅要考虑 cur 、curleft 、 parent 还要引入 grandfather,具体为什么看到后面就知道了

2. 红黑树类的属性

是使用枚举枚举出 红 黑 两种情况

58533f47adb74e74bda140814c9b16bd.png

3. 红黑树的插入函数

还是分情况讨论:

4. 总结

根据性质可以分为以下情况:

可以看出第一种情况(uncle存在且为黑)只需要变色即可,在看是否继续向上调整

第二种情况就要旋转了,而如何旋转本质图中也详细说了,取决于 grandfather、parent、cur 之间的关系,其实AVL树的旋转的决定因素也在此,只是转换成平衡因子进行描述

关于红黑,具体的思想关键体现在插入函数中,复习请自行分析出两种大情况,和情况二的细分,然后写出插入函数,才算过关

总体代码:

#pragma once

#include <iostream>

using namespace std;

#include <string>

#include <array>

#include <deque>

#include <list>

#include <map>

#include <queue>

#include <set>

#include <stack>

#include <unordered_map>

#include <unordered_set>

#include <vector>


#include <algorithm>

#include <assert.h>


#include <windows.h>

#include <ctime>




namespace zhuandrong
{
	enum Color
	{
		RED,
		BLACK
	};

	template<typename K, typename T>
	class RBTreeNode
	{
		typedef RBTreeNode Node;

	public:
		pair<K, T> _t;

		Node* _left;
		Node* _right;
		Node* _parent;
		
		Color color;

	public:
		
		RBTreeNode(pair<K, T> t)
			: _t(t)
			, _left(nullptr)
			, _right(nullptr)
			, _parent(nullptr)
			, color(RED)
		{}

	};


	template<typename K, typename T>
	class RBTree
	{
		typedef RBTreeNode<K, T> Node;

	protected:
		
		Node* _root;

	protected:
		
		void RotateL(Node* parent)
		{
			assert(parent);

			Node* cur = parent->_right;
			Node* cur_left = cur->_left;

			// 改变节点之间的关系

			parent->_right = cur_left;
			cur->_left = parent;


			// 改变_parent的指向

			//原本 parent 的 _parent
			Node* ppNode = parent->_parent;

			//若原本 ppNode(原本 parent 的 _parent)为空,说明parent原本不是根,现需把 ppNode 给到 cur 的 _parent,并且把 ppNode 的子更新为 cur
			if (ppNode)
			{
				if (ppNode->_left == parent)
				{
					ppNode->_left = cur;
				}
				else
				{
					ppNode->_right = cur;
				}

				cur->_parent = ppNode;
			}
			//若原本 parent->_parent 为空,说明parent原本是根,现需把_root更新为根
			else
			{
				_root = cur;
				cur->_parent = nullptr;
			}

			if (cur_left)
			{
				cur_left->_parent = parent;
			}

			parent->_parent = cur;

		}

		void RotateR(Node* parent)
		{
			assert(parent);

			Node* cur = parent->_left;
			Node* cur_right = cur->_right;

			// 改变节点之间的关系

			parent->_left = cur_right;
			cur->_right = parent;


			// 改变_parent的指向

			//原本 parent 的 _parent
			Node* ppNode = parent->_parent;

			//若原本 ppNode(原本 parent 的 _parent)为空,说明parent原本不是根,现需把 ppNode 给到 cur 的 _parent,并且把 ppNode 的子更新为 cur
			if (ppNode)
			{
				if (ppNode->_left == parent)
				{
					ppNode->_left = cur;
				}
				else
				{
					ppNode->_right = cur;
				}

				cur->_parent = ppNode;
			}
			//若原本 parent->_parent 为空,说明parent原本是根,现需把_root更新为根
			else
			{
				_root = cur;
				cur->_parent = nullptr;
			}

			if (cur_right)
			{
				cur_right->_parent = parent;
			}

			parent->_parent = cur;

		}

		void RotateRL(Node* parent)
		{
			RotateR(parent->_right);
			RotateL(parent);
		}


		void RotateLR(Node* parent)
		{
			RotateL(parent->_left);
			RotateR(parent);
		}


	public:
		
		RBTree()
			: _root(nullptr)
		{}

		RBTree(vector <pair<K, T>> v)
		{
			for (auto& e : v)
			{
				if (e.second == 5)
				{
					int i = 0;
				}

				insert(e);
			}
		}

		bool insert(const pair<K, T> t)
		{
			// 先判断根节点是否为空
			if (_root)
			{
				// 若不为空,先找到合适的插入位置
				Node* cur = _root;
				Node* parent = nullptr;

				while (cur)
				{
					if (t.second < cur->_t.second)
					{
						parent = cur;
						cur = cur->_left;
					}
					else if (t.second > cur->_t.second)
					{
						parent = cur;
						cur = cur->_right;
					}
					else
					{
						//cout << "该元素已经插入,请重试" << endl;
						return false;
					}
				}

					//找到该位置后进行插入并且链接关系
					cur = new Node(t);
					cur->_parent = parent;

					if (t.second < parent->_t.second)
					{
						parent->_left = cur;
					}
					else
					{
						parent->_right = cur;
					}
					

					// 进行调整

					while (parent && parent->color == RED)
					{
						Node* grandfather = parent->_parent;
						Node* uncle = nullptr;

						if (parent == grandfather->_left)
						{
							uncle = grandfather->_right;
						}
						else
						{
							uncle = grandfather->_left;
						}
						
						// 第一种情况:如果uncle存在且为红
						if (uncle && uncle->color == RED)
						{

							//将p和u变黑,g变红
							parent->color = uncle->color = BLACK;
							grandfather->color = RED;

							//更新,并且判断更新后的parent是否为根,不为根就继续调整;为根表明调整结束
							cur = grandfather;
							parent = cur->_parent;

							if (parent == _root)
							{
								break;
							}

						}
						// 第二种情况:如果uncle存在且为黑色或者uncle不存在
						else
						{
							// 当 cur 为 parent 的左孩子时
							if (parent->_left == cur)
							{
								if (grandfather->_left == parent)
								{
									//       g
									//    p     u
									// c
									//
									Node* uncle = grandfather->_right;

									RotateR(grandfather);

									grandfather->color = cur->color = RED;
									parent->color = BLACK;

								}
								else
								{
									//      g             g                c
									//   u     p   --> u     c     -->  g     p
									//        c                 p      u
									//
									Node* uncle = grandfather->_left;

									RotateRL(grandfather);

									grandfather->color = parent->color = RED;
									cur->color = BLACK;

								}

							}
							// 当 cur 为 parent 的右孩子时
							else
							{
								if (grandfather->_right == parent)
								{
									//    g     
									// u     p     
									//        c
									//
									Node* uncle = grandfather->_right;

									RotateL(grandfather);

									grandfather->color = cur->color = RED;
									parent->color = BLACK;

								}
								else
								{
									//      g              g                c
									//   p     u   -->  c     u     -->  p     g
									//    c            p                         u
									//
									Node* uncle = grandfather->_left;

									RotateLR(grandfather);

									grandfather->color = parent->color = RED;
									cur->color = BLACK;

								}
							}

							break;
						}
						
						
					}

			}
			// 若为空,则该元素作为根节点
			else
			{
				_root = new Node(t);
			}

			_root->color = BLACK;

			return true;
		}



		bool judge_color(Node* root, int blacksum, int blacknum)
		{
			if (!root)
			{
				if (blacknum != blacksum)
				{
					return false;
				}

				return true;
			}

			if (root->color == BLACK)
			{
				++blacknum;
			}

			if (root->color == RED && root->_parent && root->_parent->color == RED)
			{
				return false;
			}

			return judge_color(root->_left, blacksum, blacknum)
				&& judge_color(root->_right, blacksum, blacknum);
		}


		bool isBalance()
		{
			if (!_root)
			{
				cout << "根为空" << endl;
				return false;
			}

			if (_root->color == RED)
			{
				cout << "根的颜色为红色,非法" << endl;
				return false;
			}

			Node* cur = _root;
			int blacksum = 0;

			while (cur)
			{
				if (cur->color == BLACK)
				{
					++blacksum;
				}

				cur = cur->_right;
			}

			return judge_color(_root, blacksum, 0);
		}
			

	};

	void test_RBTree()
	{
		/*vector<pair<int, int>> v;

		v.push_back(make_pair(1, 13));
		v.push_back(make_pair(2, 8));
		v.push_back(make_pair(3, 17));
		v.push_back(make_pair(4, 1));
		v.push_back(make_pair(5, 11));
		v.push_back(make_pair(6, 15));
		v.push_back(make_pair(7, 25));
		v.push_back(make_pair(8, 6));
		v.push_back(make_pair(9, 22));
		v.push_back(make_pair(10, 27));
		v.push_back(make_pair(11, 5));

		RBTree<int, int> rb_tree(v);

		cout << rb_tree.isBalance() << endl;*/


		RBTree<int, int> rb_tree;

		srand((unsigned int)time(NULL));

		for (int i = 0; i < 1000; ++i)
		{
			rb_tree.insert(make_pair(i, rand()));
		}

		if (rb_tree.isBalance())
		{
			cout << "本次测试成功" << endl;
		}
		else
		{
			cout << "本次测试失败" << endl;
		}

	}


}

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

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

相关文章

SamSung三星笔记本NP930QCG-K02CN原装出厂OEM预装Win10系统

下载链接&#xff1a;https://pan.baidu.com/s/13GsR_r9caJkLjiWWaXa30Q?pwdncp9 系统自带指纹驱动、声卡,网卡,显卡等所有驱动、三星出厂时主题壁纸、系统属性三星专属LOGO标志、Office办公软件等三星出厂时自带的预装程序 由于时间关系,绝大部分资料没有上传&#xff0c;不…

SpringCloud: sentinel热点参数限制

一、定义controller package cn.edu.tju.controller;import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.springframework.web.bind.annotation.PathVariable; import org.springframewo…

出详图和工程图(上)-SOLIDWORKS 2024新功能

保持尺寸链共线 即使空间有限&#xff0c;您也可以确保尺寸链保持共线。 当尺寸文本和箭头重叠时&#xff0c;您可以选择最合适的选项。 要在尺寸文本重叠时保持尺寸链共线&#xff1a; 1. 单击工具 > 选项 > 文档属性 > 尺寸 > 线性 > 尺寸链。 2. 在共线选…

Nginx的安装——window环境

1、下载Nginx 在官网下载稳定版本&#xff1a; http://nginx.org/en/download.html 以nginx/Windows-1.24.0为例&#xff0c;直接下载 nginx-1.24.0.zip。 下载后解压&#xff0c;解压后如下&#xff1a; 2、启动nginx 在window环境下启动nginx的方法有以下两种&#xff1a; …

Hadoop3教程(二十):MapReduce的工作机制总结

文章目录 &#xff08;109&#xff09;MapTask工作机制&#xff08;110&#xff09;ReduceTask工作机制&并行度ReduceTask工作机制MapTask和ReduceTask的并行度决定机制 &#xff08;122&#xff09;MapReduce开发总结参考文献 &#xff08;109&#xff09;MapTask工作机制…

数据存储工程解决

在构建大规模爬虫系统时&#xff0c;数据存储是一个至关重要的环节。面对大量数据的处理和存储&#xff0c;我们需要采取一些工程化的解决方案&#xff0c;以确保数据的有效管理和快速检索。在本文中&#xff0c;我将与大家分享一些关于大规模爬虫数据存储的解决方案&#xff0…

Linux中的主要系统调用

Linux 操作系统中就是创建进程。创建进程的系统调用叫fork。在 Linux 里&#xff0c;要创建一个新的进程&#xff0c;需要一个老的进程调用 fork 来实现&#xff0c;其中老的进程叫作父进程&#xff08;Parent Process&#xff09;&#xff0c;新的进程叫作子进程&#xff08;C…

【刷题篇】回溯算法(广度优先搜索(一))

文章目录 N 叉树的层序遍历腐烂的橘子单词接龙打开转盘锁 N 叉树的层序遍历 给定一个 N 叉树&#xff0c;返回其节点值的层序遍历。&#xff08;即从左到右&#xff0c;逐层遍历&#xff09;。 树的序列化输入是用层序遍历&#xff0c;每组子节点都由 null 值分隔&#xff08;参…

AADL 端到端流延迟分析示例项目 Latency-case-study 简述

一、项目概述 latency-case-study 项目是一个增量延迟分析系统。该系统从系统的总体框架开始&#xff0c;逐步迭代增进&#xff0c;最终建立起系统的模型并实现对模型的分析。&#xff08;个人觉得这个过程有一些类似于“自顶向下&#xff0c;逐步求精”的过程&#xff09; 示…

它来了,xhadmin多应用Saas框架正式发布!

这是一个很激动人心的时刻&#xff0c;因为这个框架从立项到现在已经一年多的时间了&#xff0c;先给大家欣赏下颜值吧。 上图所示是总后台的首页截图&#xff0c;看上去还是比较满意的&#xff0c;不过后续还有一些小小的调整。 应用市场的效果也是相当炸裂的&#xff0c;整体…

Windows10找不到msvcr100.dll如何修复呢?详细有效的修复方法分享

在使用Windows10操作系统的过程中&#xff0c;我们经常会遇到各种各样的技术问题。其中&#xff0c;一些常见的问题是关于系统文件丢失或损坏&#xff0c;如“找不到msvcr100.dll”的错误。这个问题在Windows 10系统中尤其常见&#xff0c;msvcr100.dll是一个动态链接库文件&am…

用友NC-Cloud uploadChunk 任意文件上传漏洞

一、漏洞描述 用友 NC Cloud&#xff0c;大型企业数字化平台&#xff0c; 聚焦数字化管理、数字化经营、数字化商业&#xff0c;帮助大型企业实现 人、财、物、客的全面数字化&#xff0c;从而驱动业务创新与管理变革&#xff0c;与企业管理者一起重新定义未来的高度。为客户提…

收集灵感都有哪些网站推荐?

设计是一件非常令人兴奋的事情。特别是最常见的平面设计&#xff0c;作为一种传达想法或信息的视觉表达形式&#xff0c;被要求不仅突出个性和主题&#xff0c;而且具有创造力和美感&#xff0c;使许多设计师在灵感枯竭时疯狂。此时&#xff0c;浏览一些平面设计网站&#xff0…

YOLOv8涨点技巧:手把手教程,注意力机制如何在不同数据集上实现涨点的工作,内涵多种网络改进方法

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文独家改进&#xff1a;手把手教程&#xff0c;解决注意力机制引入到YOLOv8在自己数据集不涨点的问题点&#xff0c;本文提供五种改进方法来解决此问题&#xff1b; ContextAggregation | 亲测在血细胞检测项目中涨点&#xff0c;…

C/C++文件操作(细节满满,part2)

该文章上一篇&#xff1a;C/C文件操作&#xff08;细节满满&#xff0c;part1&#xff09;_仍有未知等待探索的博客-CSDN博客 个人主页&#xff1a;仍有未知等待探索_C语言疑难,数据结构,小项目-CSDN博客 专题分栏&#xff1a;C语言疑难_仍有未知等待探索的博客-CSDN博客 目录 …

AGI 黑客松收官,Zilliz 向量数据库助力34支参赛队伍角逐大模型时代的Killer App

紧张的 72 小时、34 组 AGI 前沿玩家、天马行空的创意、一场酣畅淋漓的黑客松比赛&#xff01; 近期&#xff0c;由 Zilliz、Founder Park、智谱 AI 联合主办的 AGI Playground Hackathon 在北京圆满落幕。作为此次大赛的主办方之一&#xff0c;Zilliz 为选手提供了开箱即用的向…

Tuxera NTFS2024Mac电脑磁盘读写工具

NTFS是一种广泛用于Windows系统的文件系统&#xff0c;但是如果你想在Mac系统中读写NTFS格式的硬盘或U盘&#xff0c;你会发现Mac系统并不支持NTFS读写。这时&#xff0c;NTFS for Mac就变得非常有用了。本文将介绍NTFS for Mac有什么用&#xff0c;NTFS for Mac怎么用。 一、…

Java8实战-总结42

Java8实战-总结42 用Optional取代null应用 Optional 的几种模式默认行为及解引用 Optional 对象两个 Optional 对象的组合使用 filter 剔除特定的值 用Optional取代null 应用 Optional 的几种模式 默认行为及解引用 Optional 对象 采用orElse方法读取这个变量的值&#xff0…

接口自动化测试持续集成,Soapui接口功能测试参数化

按照自动化测试分层实现的原理&#xff0c;每一层的脚本实现都要进行参数化&#xff0c;自动化的目标就是要实现脚本代码与测试数据分离。当测试数据进行调整的时候不会对脚本的实现带来震荡&#xff0c;从而提高脚本的稳定性与灵活度&#xff0c;降低脚本的维护成本。Soapui最…

手撕Vue-编译指令数据

经过上一篇的分析&#xff0c;完成了查找指令和模板的功能&#xff0c;接下来就是编译指令的数据了。 所以本章节主要处理的方法则是 buildElement 方法&#xff0c;我们先分析一下我们所拿到的数据在进行编码&#xff0c;这样会更加清晰一些。 我将 name, value 打印出来&…