首页 > 其他分享 >STL05——手写一个简单版本的红黑树(500+行代码)

STL05——手写一个简单版本的红黑树(500+行代码)

时间:2024-09-27 19:49:49浏览次数:3  
标签:黑树 node STL05 parent Color right 节点 500 left

STL05——手写一个简单版本的红黑树

题目描述

在STL中,红黑树是一个重要的底层数据结构,本题需要设计一个 RedBlackTree 类,实现如下功能:

1、基础功能

  • 构造函数:初始化 RedBlackTree 实例
  • 析构函数:清理资源,确保无内存泄露

2、核心功能

  • 在 RedBlackTree 中插入一个节点
  • 在 RedBlackTree 中删除一个节点
  • 查询 RedBlackTree 中某个节点是否存在
  • 获取 RedBlackTree 中节点的数量
  • 获取 RedBlackTree 中所有的节点

RedBlackTree 中的节点拥有两个属性,一个是 key,一个是 value,本题题意规定如果 key 相同,则代表是同一个节点。

输入描述

题目的包含多行输入,第一行为正整数 N, 代表后续有 N 行命令序列。

接下来 N 行,每行包含一个命令,命令格式为 [operation] [parameters] ,具体命令如下:

**
**

insert 命令:

  • 格式:insert [key] [value]
  • 功能:在 RedBlackTree 中添加节点,如果节点已经存在,则不进行任何操作

remove 命令:

  • 格式:remove [key]
  • 功能:删除 RedBlackTree 中的节点,如果节点不存在,则不进行任何操作

at 命令:

  • 格式:at [key]
  • 功能:查询 RedBlackTree 中的节点

size 命令:

  • 格式:size
  • 功能:获取 RedBlackTree 中节点的数量

print 命令:

  • 格式:print
  • 功能:按照中序遍历输出 RedBlackTree 中所有节点,格式为 [key1] [value1] [key2] [value2]

输出描述

输出为每行命令执行后的结果,具体输出格式如下:

insert 命令: 无输出

remove 命令: 无输出

at 命令: 输出一个整数,独占一行,代表 RedBlackTree 中 key 对应的 value,如果 key 不存在,则输出 not exsit

size 命令: 输出一个整数,独占一行,代表 RedBlackTree 中节点的数量

print 命令: 按照中序遍历输出 RedBlackTree 中所有节点的 key 和 value,格式为 [key1] [value1] [key2] [value2]…每个数字后都跟有一个空格,输出独占一行,如果 RedBlackTree 中不包含任何节点,则输出 empty

#include <iostream>
#include <sstream>
#include <string>
using namespace std;

enum class Color
{
	RED,
	BLACK
};

template<typename Key ,typename Value>
class RedBlackTree
{
	class Node 
	{
	public:
		Key key;
		Value value;
		Color color;
		Node* left;
		Node* right;
		Node* parent;
		
		Node()
			:color(Color::BLACK),left(NULL),right(NULL),parent(NULL)
		{}
		Node(const Key&key,const Value&value,Color color,Node*p=NULL)
			:key(key),value(value),color(color),left(NULL),right(NULL),parent(p)
		{}
	};

private:
	Node* root;
	size_t size;
	Node* Nil;//叶子结点

	//查询某结点
	Node* lookUp(const Key key)
	{
		Node* cmpNode = root;//从根节点开始
		while (cmpNode != NULL)
		{
			if (key < cmpNode->key)
				cmpNode = cmpNode->left;
			else if (key > cmpNode->key)
				cmpNode = cmpNode->right;
			else
				return cmpNode;
		}
		return NULL;
	}

	//右旋函数
	void rightRotate(Node* node)
	{
		Node* p = node->left;
		//处理p的右孩子
		node->left = p->right;
		if (p->right != NULL)
			p->right->parent = node;
		//把p置为老大
		p->parent = node->parent;
		if (node->parent == NULL)
			root = p;
		if (node == node->parent->left)
			node->parent->left = p;
		else if (node == node->parent->right)
			node->parent->right = p;
		//让node成为p的右孩子
		p->right = node;
		node->parent = p;
	}

	//左旋函数
	void leftRotate(Node* node)
	{
		Node* p = node->right;
		node->right = p->left;
		if (p->left != NULL)
			p->left->parent = node;
		p->parent = node->parent;
		if (node->parent == NULL)
			root = p;
		if (node == node->parent->left)
			node->parent->left = p;
		else if (node == node->parent->right)
			node->parent->right = p;
		p->left = node;
		node->parent = p;
	}

	//将插入的函数修复
	void insertFixup(Node*target)
	{
		while (target->parent != NULL && target->parent->color == Color::RED)
		{
			//父为祖左节点
			if (target->parent = target->parent->parent->left)
			{
				Node* uncle = target->parent->parent->right;
				//红叔
				if (uncle && uncle->color == Color::RED)
				{
					uncle->color == Color::BLACK;
					target->parent->color == Color::BLACK;
					target->parent->parent->color = Color::RED;
					target = target->parent->parent;
				}
				//黑叔
				else
				{
					//LR
					if (target == target->parent->right)
					{
						leftRotate(target->parent);
						rightRotate(target->parent);
						target->color = Color::BLACK;
						target->right->color = Color::RED;
					}
					//LL
					else if (target == target->parent->left)
					{
						rightRotate(target->parent->parent);
						target->parent->color = Color::BLACK;
						target->parent->right->color = Color::RED;
					}
				}
			}
			//父为祖的右节点
			else
			{
				Node* uncle = target->parent->left;
				//红叔
				if (uncle && uncle->color == Color::RED)
				{
					uncle->color == Color::BLACK;
					target->parent->color == Color::BLACK;
					target->parent->parent->color = Color::RED;
					target = target->parent->parent;
				}
				//黑叔
				else
				{
					//RL
					if (target == target->parent->left)
					{
						rightRotate(target->parent);
						leftRotate(target->parent);
						target->color = Color::BLACK;
						target->left->color = Color::RED;
					}
					//RR
					else if (target == target->parent->right)
					{
						leftRotate(target->parent->parent);
						target->parent->color = Color::BLACK;
						target->parent->left->color = Color::RED;
					}
				}	
			}
		}
		root->color = Color::BLACK;
	}
	void insertNode(const Key& key, const Value& value)
	{
		Node* newNode = new Node(key, value, Color::RED);
		Node* cur = root;
		if (root == NULL)
			root = newNode;
		while (cur->left!=NULL||cur->right!=NULL)
		{
			if (cur->key > newNode->key)
				cur = cur ->left;
			else if (cur->key < newNode->key)
				cur = cur->right;
			else
			{
				delete newNode;
				return;
			}
		}
		if (cur->key > newNode->key)
			cur->right = newNode;
		else
			cur->left = newNode;
		size++;
	}

	//中序遍历
	void inorderTraversal(Node* node) const
	{
		if (node != NULL)
		{
			inorderTraversal(node->left);
			cout << node->key << " ";
			cout << node->value << " ";
			inorderTraversal(node->right);
		}
	}
	
	//用新节点替换旧结点(旧结点不用再找了)
	void replaceNode(Node* target, Node* newNode)
	{
		if (target->parent == NULL)
			root = newNode;
		if (target == target->parent->left)
			target->parent->left = newNode;
		else if (target == target->parent->right)
			target->parent->right = newNode;
		if (newNode != NULL)
			newNode->parent = target->parent;
	}

	Node* findMinimumNode(Node* node)
	{
		while (node->left != NULL)
		{
			node = node->left;
		}
		return node;
	}

	// removeFixup函数用于在删除节点后恢复红黑树的性质
	void removeFixup(Node* node) {
		// 如果节点为Nil并且没有父节点,说明它是唯一的节点,直接返回
		if (node == Nil && node->parent == nullptr) {
			return;
		}

		// 当我们没有到达根节点时继续循环
		while (node != root) {
			// 如果节点是其父节点的左子节点
			if (node == node->parent->left) {
				// 兄弟节点是节点父亲的右子节点
				Node* sibling = node->parent->right;

				// 情况1:节点的兄弟节点是红色
				if (getColor(sibling) == Color::RED) {
					// 重新着色兄弟节点和父节点,并进行左旋
					setColor(sibling, Color::BLACK);
					setColor(node->parent, Color::RED);
					leftRotate(node->parent);
					// 旋转后更新兄弟节点
					sibling = node->parent->right;
				}

				// 情况2:兄弟节点的两个子节点都是黑色
				if (getColor(sibling->left) == Color::BLACK &&
					getColor(sibling->right) == Color::BLACK) {
					// 重新着色兄弟节点并向上移动
					setColor(sibling, Color::RED);
					node = node->parent;
					// 如果父节点是红色,将其改为黑色并结束
					if (node->color == Color::RED) {
						node->color = Color::BLACK;
						node = root;
					}
				}
				else {
					// 情况3:兄弟节点的右子节点是黑色(左子节点是红色)
					if (getColor(sibling->right) == Color::BLACK) {
						// 重新着色兄弟节点和兄弟节点的左子节点,并进行右旋
						setColor(sibling->left, Color::BLACK);
						setColor(sibling, Color::RED);
						rightRotate(sibling);
						// 旋转后更新兄弟节点
						sibling = node->parent->right;
					}

					// 情况4:兄弟节点的右子节点是红色
					setColor(sibling, getColor(node->parent));
					setColor(node->parent, Color::BLACK);
					setColor(sibling->right, Color::BLACK);
					leftRotate(node->parent);
					// 移动到根节点结束
					node = root;
				}
			}
			else {
				// 当节点是其父节点的右子节点时,对称的情况
				Node* sibling = node->parent->left;

				if (getColor(sibling) == Color::RED) {
					setColor(sibling, Color::BLACK);
					setColor(node->parent, Color::RED);
					rightRotate(node->parent);
					sibling = node->parent->left;
				}

				if (getColor(sibling->right) == Color::BLACK &&
					getColor(sibling->left) == Color::BLACK) {
					setColor(sibling, Color::RED);
					node = node->parent;
					if (node->color == Color::RED) {
						node->color = Color::BLACK;
						node = root;
					}
				}
				else {
					if (getColor(sibling->left) == Color::BLACK) {
						setColor(sibling->right, Color::BLACK);
						setColor(sibling, Color::RED);
						leftRotate(sibling);
						sibling = node->parent->left;
					}
					setColor(sibling, getColor(node->parent));
					setColor(node->parent, Color::BLACK);
					setColor(sibling->left, Color::BLACK);
					rightRotate(node->parent);
					node = root;
				}
			}
		}
		// 确保当前节点是黑色的,以维持红黑树性质
		setColor(node, Color::BLACK);
	}

	Color getColor(Node* node)
	{
		if (node == NULL)
			return Color::BLACK;
		else
			return node->color;
	}

	void setColor(Node* node, Color color) {
		if (node == nullptr) {
			return;
		}
		node->color = color;
	}
	//取消Nil的连接
	void dieConnectNil()
	{
		if (Nil == NULL)
			return;
		if (Nil->parent != NULL)
		{
			if (Nil == Nil->parent->left)
				Nil->parent->left = NULL;
			else
				Nil->parent->right = NULL;
		}
	}

	// 删除节点
	void deleteNode(Node* del) {
		Node* rep = del; // rep(替代节点)初始指向要删除的节点
		Node* child = nullptr;      // 要删除节点的孩子节点
		Node* parentRP;             // 替代节点的父节点
		Color origCol = rep->color; // 保存要删除节点的原始颜色

		// 如果删除节点没有左孩子
		if (!del->left) {
			rep = del->right;        // 替代节点指向删除节点的右孩子
			parentRP = del->parent;  // 更新替代节点的父节点
			origCol = getColor(rep); // 获取替代节点的颜色
			replaceNode(del, rep);   // 用替代节点替换删除节点
		}
		// 如果删除节点没有右孩子
		else if (!del->right) {
			rep = del->left;         // 替代节点指向删除节点的左孩子
			parentRP = del->parent;  // 更新替代节点的父节点
			origCol = getColor(rep); // 获取替代节点的颜色
			replaceNode(del, rep);   // 用替代节点替换删除节点
		}
		// 如果删除节点有两个孩子
		else {
			rep = findMinimumNode(
				del->right); // 找到删除节点右子树中的最小节点作为替代节点
			origCol = rep->color; // 保存替代节点的原始颜色
			// 如果替代节点不是删除节点的直接右孩子
			if (rep != del->right) {
				parentRP = rep->parent; // 更新替代节点的父节点
				child = rep->right; // 替代节点的右孩子变成要处理的孩子节点
				parentRP->left =
					child; // 替代节点的父节点的左孩子指向替代节点的孩子(因为替代节点是最小节点,所以不可能有左孩子)
				if (child != nullptr) {
					child->parent = parentRP; // 如果替代节点的孩子存在,则更新其父节点
				}
				// 将替代节点放到删除节点的位置
				del->left->parent = rep;
				del->right->parent = rep;
				rep->left = del->left;
				rep->right = del->right;
				// 如果删除节点有父节点,更新父节点的孩子指向
				if (del->parent != nullptr) {
					if (del == del->parent->left) {
						del->parent->left = rep;
						rep->parent = del->parent;
					}
					else {
						del->parent->right = rep;
						rep->parent = del->parent;
					}
				}
				// 如果删除节点没有父节点,说明它是根节点
				else {
					root = rep;
					root->parent = nullptr;
				}
			}
			// 如果替代节点是删除节点的直接右孩子
			else {
				child = rep->right; // 孩子节点指向替代节点的右孩子
				rep->left = del->left; // 替代节点的左孩子指向删除节点的左孩子
				del->left->parent = rep; // 更新左孩子的父节点
				// 更新删除节点父节点的孩子指向
				if (del->parent != nullptr) {
					if (del == del->parent->left) {
						del->parent->left = rep;
						rep->parent = del->parent;
					}
					else {
						del->parent->right = rep;
						rep->parent = del->parent;
					}
				}
				// 如果删除节点是根节点
				else {
					root = rep;
					root->parent = nullptr;
				}
				parentRP = rep; // 更新替代节点的父节点
			}
		}

		// 如果替代节点存在,更新其颜色为删除节点的颜色
		if (rep != nullptr) {
			rep->color = del->color;
		}
		// 如果替代节点不存在,将删除节点的颜色赋给origCol变量
		else {
			origCol = del->color;
		}

		// 如果原始颜色是黑色,需要进行额外的修复操作,因为黑色节点的删除可能会破坏红黑树的性质
		if (origCol == Color::BLACK) {
			// 如果存在孩子节点,进行修复操作
			if (child != nullptr) {
				removeFixup(child);
			}
			// 如果不存在孩子节点,将Nil节点(代表空节点)的父节点设置为替代节点的父节点
			else {
				Nil->parent = parentRP;
				// 如果替代节点的父节点存在,设置其对应的孩子指针为Nil节点
				if (parentRP != nullptr) {
					if (parentRP->left == nullptr) {
						parentRP->left = Nil;
					}
					else {
						parentRP->right = Nil;
					}
				}
				// 进行修复操作
				removeFixup(Nil);
				// 断开Nil节点与树的连接,因为在红黑树中Nil节点通常是单独存在的
				dieConnectNil();
			}
		}

		// 删除节点
		delete del;
	}

	public:
		RedBlackTree() :root(NULL), size(0), Nil(new Node())
		{
			Nil->color = Color::BLACK;
		}

		~RedBlackTree()
		{
			deleteTree(root);
		}

		void insert(const Key& key, const Value& value) 
		{ 
			insertNode(key, value); 
		}

		void remove(const Key& key)
		{
			Node* p = lookUp(key);
			if (p != NULL)
			{
				deleteNode(p);
				size--;
			}
		}

		Value* at(const Key& key)
		{
			auto ans = lookUp(key);
			if (ans == NULL)
				return NULL;
			else
				return &ans->value;
		}

		int getSize() { return size; }

		bool empty() { return size == 0; }

		void print() {
			inorderTraversal(root);
			std::cout << std::endl;
		}

		void clear()
		{
			deleteNode(root);
			size = 0;
		}
		private:
			void deleteTree(Node* node)
			{
				if (node)
				{
					deleteTree(node->left);
					deleteTree(node->right);
					delete node;
				}
			}
};
int main() {
	// 创建红黑树实例
	RedBlackTree<int, int> rbTree;

	int N;
	std::cin >> N;
	getchar();

	std::string line;
	for (int i = 0; i < N; i++)
	{
		std::getline(std::cin, line);
		std::istringstream iss(line);
		std::string command;
		iss >> command;

		int key;
		int value;

		if (command == "insert")
		{
			iss >> key >> value;
			rbTree.insert(key, value);
		}

		if (command == "size")
		{
			std::cout << rbTree.getSize() << std::endl;
		}

		if (command == "at")
		{
			iss >> key;
			int* res = rbTree.at(key);
			if (res == nullptr)
			{
				std::cout << "not exist" << std::endl;
			}
			else
			{
				std::cout << *res << std::endl;
			}
		}

		if (command == "remove")
		{
			iss >> key;
			rbTree.remove(key);
		}

		if (command == "print")
		{
			if (rbTree.empty())
			{
				std::cout << "empty" << std::endl;
			}
			else
			{
				rbTree.print();
			}
		}
	}
	return 0;
}

标签:黑树,node,STL05,parent,Color,right,节点,500,left
From: https://blog.csdn.net/2402_84438596/article/details/142601216

相关文章

  • 红黑树|定义、左旋函数、右旋函数和对插入结点的修复
    红黑树|定义、左旋函数、右旋函数和对插入结点的修复1.红黑树类的定义2.左旋函数和右旋函数3.对插入结点的修复1.红黑树类的定义enumclassColor{ RED, BLACK};template<typenameKey,typenameValue>classRedBlackTree{ classNode { public: Key......
  • 封碳破5000万立方米!,把二氧化碳回注海底!
    什么?人类在收集空气?!!收集的二氧化碳用来干什么?这活咋干?近期了解到一则新闻:不得不说人类的智慧真是令人感叹,我们确实在为地球的环保事业添砖加瓦,点点滴滴的努力都在积累,从日常生活的点滴到工业生产的革新,我们正加快脚步,积极投入其中。【低碳减排】正以倍道而进的速度推进:简单说有两种......
  • 大师级调色预设合集!50000+款Lr预设,精心整理,分类清晰,各种风格都有!
       大师级调色预设合集!超过50000种风格,覆盖550多个不同的分类,并且还在持续更新,非常齐全,用一生都足够了。这些预设是我长期筛选和整理的结果,它们分门别类、井井有条,拿来即用,无论是日系风格的清新文艺范,还是INS上的流行网红色调,或是专门针对人像、美食摄影的预设,甚至......
  • 红黑树
    #include"common.h"typedefstructrb_node_trb_node_t;structrb_node_t{rb_node_t*m_parent;rb_node_t*m_left;rb_node_t*m_right;boolm_red;intm_value;};rb_node_t*rb_node_new(rb_node_t*parent,intvalue){......
  • 《 C++ 修炼全景指南:十二 》用红黑树加速你的代码!C++ Set 和 Map 容器从入门到精通
    摘要本文详细介绍了基于红黑树实现的Set和Map容器,包括其底层设计原理、插入和删除操作的实现细节、性能分析与优化策略,以及实际应用场景和未来发展方向。通过采用红黑树的数据结构,Set和Map容器能够高效地处理有序数据,保持O(logn)的时间复杂度,适用于各种数据存储......
  • ORA-38500: USING CURRENT LOGFILE option not available without stand
    在dataguard启用实时恢复的时候,报如下错误:ORA-38500:USINGCURRENTLOGFILEoptionnotavailablewithoutstand实际操作:SQL>alterdatabaserecovermanagedstandbydatabaseusingcurrentlogfiledisconnectfromsession;alterdatabaserecovermanagedstandbydata......
  • Java集合类面试题:Map接口(链表、HashMap、红黑树)
    收集大量Java经典面试题目......
  • CertiK:Ventures宣布4500万美元投资计划,Token Scan等社区安全工具免费开放
    2024年9月19日,Web3.0头部安全公司CertiK在Token2049期间联合CertiKVentures、OKXVentures和OKXWallet举办了“NewRound,NewPath”活动。CertiK宣布,将对产品服务进行全面升级,覆盖Web3.0项目的全生命周期;CertiK旗下备受瞩目的CertiKVentures将投入4500万美元,全力支持高......
  • 帝国备份王安装 /diguo 返回http500错误原因及解决方法
    当你在使用帝国备份王进行数据恢复时遇到/diguo路径返回HTTP500错误,这通常表示服务器端发生了某种错误。根据已有的信息,HTTP500错误的原因可能与PHP的配置有关,特别是short_open_tag的设置。HTTP500错误原因HTTP500错误通常表明服务器遇到了意料之外的情况,阻止了它完成请求......
  • 国人疯抢!问界M9五座版72小时大定超5000台
    文|AUTO芯球作者|响铃&雷慢要不是真发生了我是真不敢想啊鸿蒙智行官方公布,问界M9大定突破14万台,交付也达到10万台,这什么概念?50万以上的豪华车,问界M9稳稳的站在第一名,其他前20名销量加起来也没它一款多。作为一个问界M9车主,我真是为国产车自豪啊你们看啊,8月份问界M9交付了将近1.7......