红黑树封装实现map和set

news2024/11/19 19:24:26

全文目录

  • map和set
  • 红黑树的封装
  • 红黑树的迭代器
    • begin() 和 end()
    • operator++()
    • operator--()
  • map完整代码
  • set完整代码
  • 改造后的红黑树代码

map和set

map和set 是STL中的容器,两者底层的实现都是参考文档:


map参考文档

在这里插入图片描述


set参考文档

在这里插入图片描述

两个容器底层都是去重的二叉搜索树实现的,一般使用高效的红黑树通过封装来实现。

红黑树的封装

先看一下原码:

在这里插入图片描述

在STL的原码中红黑树是有两个数据类型的树,map和set是通过控制模板类型的传递来生成不同数据类型的红黑树。

map传递:key 和 pair<key, value>

set传递: key 和 key

但是这样有导致了红黑树比较时不知道是使用单类型还是pair类来进行的比较,所以在参数传递时还会将传递仿函数,指定使用key进行比较

map的比较仿函数:

template<typename K, typename V>
struct MapValueOfT
{
	const K& operator()(const pair<K, V>& kv) const
	{
		return kv.first;
	}
};

set的比较类仿函数:

template<typename K>
struct SetValueOfT
{
	const K& operator()(const K& key) const
	{
		return key;
	}
};

那么在两者内部,红黑树都是这样的结构:

map的底层红黑树:

template<typename K, typename V> 
class map
{
public:
	typedef pair<K, V> valueType;
	typedef RBTree<K, valueType, MapValueOfT<K, V>> Tree;
	Tree _t;
}

set底层的红黑树:

template<typename K>
class set
{
public:
	typedef K valueType;
	typedef RBTree<K, valueType, SetValueOfT<K>> Tree;
	Tree _t;
}

红黑树的迭代器

begin() 和 end()

STL明确规定,begin()end()代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,可以得到一个有序的序列,因此:begin()可以放在红黑树中最小节点(即最左侧节点)的位置end() 放在最大节点(最右侧节点)的下一个位置,关键是最大节点的下一个位置在哪块?

能否给成nullptr呢?答案是行不通的,因为对end()位置的迭代器进行–操作,必须要能找最后一个元素,此处就不行,因此最好的方式是将end()放在头结点的位置:

在这里插入图片描述

但是实现的红黑树的头结点是nullptr,我们只实现正向的迭代器:

operator++()

找迭代器的下一个节点,因为是中序遍历,所以分两种情况:

  1. 右子树存在

找右子树的最左节点

  1. 右子树不存在

右子树不存在,向上查找,直到 cur != parent->_right

Self& operator++()
{
	// 右子树不为空,就是找右子树中的最左节点
	if (_node->_right)
	{
		_node = _node->_right;
		while (_node->_left)
		{
			_node = _node->_left;
		}

	}
	else
	{
		// 右子树为空,找孩子不是父亲的右节点的父节点
		Node* parent = _node->_parent;
		while (parent && parent->_right == _node)
		{
			_node = parent;
			parent = parent->_parent;
		}
		_node = parent;
	}

	return *this;
}

operator–()

获取中序遍历的前一个节点,同样分两种情况:

  1. 左子树存在

在左子树中找最大的节点,即左子树中最右侧节点

  1. 左子树不存在

左子树不存在,向上查找,直到 cur != parent->_left

Self& operator--()
{
	// 左子树不为空,找左子树中的最右节点
	if (_node->_left)
	{
		_node = _node->_left;
		while (_node && _node->_left)
		{
			_node = _node->_left;
		}
	}
	else
	{
		// 左子树为空,找孩子不是父亲的左节点的父节点
		Node* parent = _node->_parent;
		while (parent && parent->_left == _node)
		{
			_node = parent;
			parent = parent->_parent;
		}
	}

	return *this;
}

map完整代码

template<typename K, typename V>
struct MapValueOfT
{
	const K& operator()(const pair<K, V>& kv) const
	{
		return kv.first;
	}
};

template<typename K, typename V> 
class map
{
public:
	typedef pair<K, V> valueType;
	typedef RBTree<K, valueType, MapValueOfT<K, V>> Tree;
	typedef typename RBTree<K, valueType, MapValueOfT<K, V>>::iterator iterator;

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

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



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

	void inorder()
	{
		_t.InOrder();
	}

	V& operator[](const K& key)
	{
		return (insert(make_pair(key, V())).first)->second;
	}

	Tree _t;
};

set完整代码

template<typename K>
struct SetValueOfT
{
	const K& operator()(const K& key) const
	{
		return key;
	}
};

template<typename K>
class set
{
public:
	typedef K valueType;
	typedef RBTree<K, valueType, SetValueOfT<K>> Tree;
	typedef typename RBTree<K, valueType, SetValueOfT<K>>::iterator iterator;

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

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


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

	void inorder()
	{
		_t.InOrder();
	}

	Tree _t;
};

改造后的红黑树代码

enum Color
{
	BLACK,
	RED
};
template<typename T>
struct RBNode
{
	RBNode(const T& data)
		: _data(data)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _col(RED)
	{}

	RBNode<T>* _left;
	RBNode<T>* _right;
	RBNode<T>* _parent;
	Color _col;
	T _data;
};

template<typename T, typename Ref, typename Ptr>
class Iterator
{
public:
	typedef RBNode<T> Node;
	typedef Iterator<T, Ref, Ptr> Self;

	Iterator(Node* node)
		:_node(node)
	{}

	bool operator==(const Self& it) const
	{
		return _node == it._node;
	}

	bool operator!=(const Self& it) const
	{
		return _node != it._node;
	}

	T& operator*()
	{
		return _node->_data;	
	}

	Ptr operator->()
	{
		return &(_node->_data);
	}

	Self& operator++()
	{
		// 右子树不为空,就是找右子树中的最左节点
		if (_node->_right)
		{
			_node = _node->_right;
			while (_node->_left)
			{
				_node = _node->_left;
			}

		}
		else
		{
			// 右子树为空,找孩子不是父亲的右节点的父节点
			Node* parent = _node->_parent;
			while (parent && parent->_right == _node)
			{
				_node = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}

		return *this;
	}

	Self& operator--()
	{
		// 左子树不为空,找左子树中的最右节点
		if (_node->_left)
		{
			_node = _node->_left;
			while (_node && _node->_left)
			{
				_node = _node->_left;
			}
		}
		else
		{
			// 左子树为空,找孩子不是父亲的左节点的父节点
			Node* parent = _node->_parent;
			while (parent && parent->_left == _node)
			{
				_node = parent;
				parent = parent->_parent;
			}
		}

		return *this;
	}

	Self operator++(int)
	{
		Self tmp(_node);
		this->operator++();
		return tmp;
	}

	Self operator--(int)
	{
		Self tmp(_node);
		operator--();
		return tmp;
	}

private:
	Node* _node;
};

template<typename Key, typename Value, typename ValueOfT = less<Key>>
class RBTree
{
public:
	typedef RBNode<Value> Node;
	typedef Iterator<Value, Value&, Value*> iterator;

	RBTree()
		: _root(nullptr)
	{}

	iterator begin()
	{
		Node* cur = _root;
		while (cur && cur->_left)
		{
			cur = cur->_left;
		}

		return iterator(cur);
	}

	iterator end()
	{
		return iterator(nullptr);	
	}



	pair<iterator, bool> Insert(const Value& data)
	{
		// 空树的插入
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			return make_pair(_root, true);
		} 

		// 非空树的插入
		Node* parent = nullptr;
		Node* cur = _root;

		// 寻找插入的位置
		while (cur)
		{
			parent = cur;

			if (cmp(data) > cmp(cur->_data))
			{
				cur = cur->_right;
			}
			else if (cmp(data) < cmp(cur->_data))
			{
				cur = cur->_left;
			}
			else	// 找到重复元素,插入失败
			{
				return make_pair(cur, false);
			}
		}

		// 开始插入
		cur = new Node(data);
		if (cmp(data) > cmp(parent->_data))
		{
			cur->_parent = parent;
			parent->_right = cur;
		}
		else
		{
			cur->_parent = parent;
			parent->_left = cur;
		}
		Node* newNode = cur;

		// 如果父节点是根节点,那么它的颜色一定是要黑色,不然在前面插入的时候已经出错了
		// 所以进入循环,就说明一定存在祖父节点
		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
				Node* uncle = grandfather->_right;
				if (uncle && uncle->_col == RED)	// 叔叔存在且为红,继续往上更新
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					cur = grandfather;
				}
				else	// 叔叔不存在或者叔叔为黑
				{
					if (cur == parent->_left)	// 右单旋
					{
						//       g
						//      / \
						//     p   u
						//    /
						//   c 
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else	// 左右双旋
					{
						//        g
						//       / \
						//      p   u
						// 		 \					   
						//        c
						//RotateL(parent);
						//RotateR(grandfather);
						RotateLR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
						

					break;
				}
			}
			else if (parent == grandfather->_right)
			{
				Node* uncle = grandfather->_left;
				if (uncle && uncle->_col == RED)	// 叔叔存在且为红,继续往上更新
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					cur = grandfather;
				}
				else	// 叔叔不存在或者叔叔为黑
				{
					if (cur == parent->_right)	// 左单旋
					{
						//       g
						//      / \
						//     u   p
						//          \
						//           c
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else	// 右左双旋
					{
						//       g
						//      / \
						//     u   p
						//        /
						//       c
						//RotateR(parent);
						//RotateL(grandfather);
						RotateRL(grandfather);
						cur->_col =	 BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
		}

		_root->_col = BLACK;

		return make_pair(newNode, true);
	}


	void InOrder()
	{
		_InOrder(_root);
	}

	// 根据黑色节点数量判断是否满足红黑树条件
	bool IsBalance()
	{
		if (_root && _root->_col == RED)
		{
			puts("root的颜色为红!!!");
			return false;
		}

		int balanceNum = 0;
		return Check(_root, balanceNum, 0);	
	}


private:
	// 前序遍历检查每天路径上的黑色节点数量
	bool Check(Node* root, int& balanceNum, int blackNum)
	{
		if (root == nullptr)
		{
			if (balanceNum == 0)
			{
				balanceNum = blackNum;
				return true;
			}
			else
			{
				if (blackNum == balanceNum)
					return true;
				else 
					return false;
			}
		}

		if (root->_col == BLACK)
		{
			blackNum++;
		}

		return Check(root->_left, balanceNum, blackNum)
			&& Check(root->_right, balanceNum, blackNum);
	}


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

		_InOrder(root->_left);
		cout << root->_data.first << " : " << root->_data.second << endl;
		_InOrder(root->_right);
	}

	// 左单旋
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		Node* pparent = parent->_parent;

		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		parent->_parent = subR;
		subR->_left = parent;

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


	}

	// 右单旋
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		Node* pparent = parent->_parent;


		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;

		parent->_parent = subL;
		subL->_right = parent;

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


	}


	// 左右双旋
	void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		RotateL(subL);
		RotateR(parent);	
	}

	// 右左双旋
	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		RotateR(subR);
		RotateL(parent);
	}

	ValueOfT cmp;
	Node* _root;
};

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

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

相关文章

Spring复杂对象的3中创建方法

复杂对象是相对于简单对象可以直接 new 出的对象。这种对象在 Spring 中不可以通过简单对象的创建方式来创建。下面我们将通过实现 FactoryBean 接口、实例工厂、静态工厂三种方法来创建。 FactoryBean 接口 Spring 提供 FactoryBean 接口并且提供了 getObject 方法是为了支持…

【Vue.js】生命周期与基本使用

&#x1f3ac; 艳艳耶✌️&#xff1a;个人主页 &#x1f525; 个人专栏 &#xff1a;《Spring与Mybatis集成整合》 《springMvc使用》 ⛺️ 生活的理想&#xff0c;为了不断更新自己 ! 目录 ​编辑 1.Vue是什么 2.Vue的特点及优势 3. 使用Vue的详细步骤 3.1.导入 3.2…

Vue3引入滑块验证组件-2分钟搞定

安装 npm install --save vue3-slide-verify登录页面引入 template 下 <template><div class"login"><el-card class"cover" v-if"loginUser.data.user"><slide-verifyref"block"slider-text"向右滑动-&…

实验室预约系统设计与实现

实验室预约系统的设计 摘 要 目前各大学的实验项目日益繁多&#xff0c;如何合理预约实验室&#xff0c;已经成为当今各个大学实验室课程预约的难题。因此&#xff0c;这个实验室预约系统就是研究实验室预约的相关问题。实验室预约系统的设计主要是基于B/S模型&#xff0c;在W…

MySQL--MySQL索引事务

事务的概念 事务指逻辑上的一组操作&#xff0c;组成这组操作的各个单元&#xff0c;要么全部成功&#xff0c;要么全部失败。 在不同的环境中&#xff0c;都可以有事务。对应在数据库中&#xff0c;就是数据库事务。 使用 &#xff08;1&#xff09;开启事务&#xff1a;start…

VHOST-SCSI代码分析(2)VHOST SCSI驱动分析

在HOST内核中创建/dev/vhost-scsi&#xff0c;并提供用户态相关接口&#xff0c;在文件driver/vhost/scsi.c中。 对于/dev/vhost-scsi的ioctl调用包含如下类型&#xff1a; &#xff08;1&#xff09;VRING相关的系统调用 &#xff08;2&#xff09;VHOST SCSI相关的系统调用 …

【重新定义matlab强大系列十三】直方图 bin 计数和分 bin 散点图

&#x1f517; 运行环境&#xff1a;Matlab &#x1f6a9; 撰写作者&#xff1a;左手の明天 &#x1f947; 精选专栏&#xff1a;《python》 &#x1f525; 推荐专栏&#xff1a;《算法研究》 #### 防伪水印——左手の明天 #### &#x1f497; 大家好&#x1f917;&#x1f91…

Web前端大作业html+css静态页面--掌****有限公司

文章目录 前言 一、效果图 二、代码展示 1.html 2.css部分 总结 前言 对于大一新生来说&#xff0c;期末考试和专业结课作业可能会带来很大的压力。特别是涉及到网页设计和编写的作业&#xff0c;可能让人感到无从下手。例如&#xff0c;web实训大作业、网页期末作业、web课程与…

四、C#—变量,表达式,运算符(2)

&#x1f33b;&#x1f33b; 目录 一、表达式1.1 什么是表达式1.2 表达式的基本组成 二、运算符2.1 算术运算符2.1.1 使用 / 运算符时的注意事项2.1.2 使用%运算符时的注意事项 2.2 赋值运算符2.2.1 简单赋值运算符2.2.2 复合赋值运算符 2.3 关系运算符2.4 逻辑运算符2.4.1 逻辑…

nexus 5x 刷机记录

1. 参考链接 https://d1ag0n.asia/archives/nexus5x%E5%88%B7android81root https://github.com/r0ysue/AndroidSecurityStudy/blob/master/FRIDA/A01/README.md 2. 下载工具 adb ,fastboot 官网的下载地址 &#xff1a; https://developer.android.com/studio/releases/pla…

高级视频和直播应用程序:Challenge 1.1.8 源码

您是否正在寻找高级视频和直播应用程序&#xff1f; 那么挑战就是您的完美选择。终极视频和直播挑战平台。凭借其尖端功能&#xff0c;Challenge 为用户和所有者提供了独特且引人入胜的体验。 通过购买挑战代码&#xff0c;您将可以使用以下令人兴奋的功能&#xff1a; 故事&…

学术团体的机器人相关分会和机器人相关大赛的说明

1. 中国机械工程学会 &#xff08;机器人分会&#xff09; 2017年成立&#xff0c;地点 华中科技大学 &#xff1a;中国机械工程学会机器人分会在汉成立 (huanqiu.com) 链接&#xff1a;中国机械工程学会 (cmes.org) 侧重点&#xff1a;工业机械臂、工厂和物流相关的移动机…

第 363 场 LeetCode 周赛题解

A 计算 K 置位下标对应元素的和 模拟 class Solution { public:int pop_cnt(int x) {//求x的二进制表示中的1的位数int res 0;for (; x; x >> 1)if (x & 1)res;return res;}int sumIndicesWithKSetBits(vector<int> &nums, int k) {int res 0;for (int i…

做一个有灵魂的软件测试员

有没有觉得自己每天的工作千篇一律&#xff0c;每天一上班就盼着下班&#xff1f; 一个月似乎能令自己开心的时间也就是发工资的那一天&#xff1f; 自己的工作生活总感觉被人牵着走&#xff0c;兜兜转转过了一年又一年&#xff1f; 测试员的工作性质决定了与重复、枯燥和乏…

自定义实现简易版ArrayList

文章目录 1.了解什么是顺序表2.实现哪些功能3.初始化ArrayList4.实现功能接口遍历顺序表判断顺序表是否已满添加元素指定下标添加元素自定义下标不合法异常判断顺序表是否为空查找指定元素是否存在查找指定元素返回下标获取指定下标的元素顺序表为空异常修改指定下标元素的值删…

【深度学习实验】线性模型(三):使用Pytorch实现简单线性模型:搭建、构造损失函数、计算损失值

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入库 1. 定义线性模型linear_model 2. 定义损失函数loss_function 3. 定义数据 4. 调用模型 5. 完整代码 一、实验介绍 使用Pytorch实现 线性模型搭建构造损失函数计算损失值 二、…

5-1 Dataset和DataLoader

Pytorch通常使用Dataset和DataLoader这两个工具类来构建数据管道。 Dataset定义了数据集的内容&#xff0c;它相当于一个类似列表的数据结构&#xff0c;具有确定的长度&#xff0c;能够用索引获取数据集中的元素。 而DataLoader定义了按batch加载数据集的方法&#xff0c;它是…

无涯教程-JavaScript - EVEN函数

描述 EVEN函数返回四舍五入到最接近的偶数整数的数字。您可以使用此功能来处理两个项目。 语法 EVEN (number)争论 Argument描述Required/OptionalNumberThe value to round.Required Notes 如果数字为非数字,则EVEN返回#VALUE!错误值。 不管数字的符号如何,当从零开始调…

VisualStudio配置驱动远程部署

目标机器开启ping命令 默认情况下&#xff0c;Windows出于安全考虑不允许外部主机对其进行Ping测试。 允许ICMP回显 设置如下&#xff1a; 打开win7防火墙设置界面 左边的菜单中选择 【高级设置】 在弹出的 【高级安全 Windows 防火墙】 界面&#xff0c;选择 【入站规则】 …

Java常见面试题(含答案,持续更新中~~)

目录 1、JVM、JRE和JDK的关系 2、什么是字节码&#xff1f;采用字节码的最大好处是什么 3、Java和C的区别与联系 4、Java和GO的区别与联系 5、 和 equals 的区别是什么&#xff1f; 6、Oracle JDK 和 OpenJDK 的对比 7、String 属于基础的数据类型吗&#xff1f; 8、fi…