红黑树底层封装map、set C++

目录

一、框架思考

三个问题

问题1的解决

问题2的解决:

问题3的解决:

二、泛型编程

1、仿函数的泛型编程

2、迭代器的泛型编程

3、typename:

4、++/--重载

三、原码

红黑树

map

set


一、框架思考

map和set都是使用红黑树底层,要怎么实现同一个底层,但是实现不同的功能呢?

三个问题


1、map是pair模型,而set是key模型

2、map和set的迭代器用法是一样的,如何实现?

3、插入时,set和map插入的类型不同,如何实现?

我们是一个简单的实现,而不是全部,所以抓重点,化繁为简
只关注和当前我么要实现功能有关系的部分,其他的统统不关注

问题1的解决


RBTree的节点传的是一个模板

template<class T>
struct BRTreeNode
{
	BRTreeNode<T>* _parent;
	BRTreeNode<T>* _right;
	BRTreeNode<T>* _left;
	T _data;
	Colour _col;

	BRTreeNode(const T& data)
		:_parent(nullptr)
		, _right(nullptr)
		, _left(nullptr)
		, _data(data)
		, _col(RED)
	{
	}

};

map传<K,pair<K,V>>
set传<K,K>

我set要用的是key模型的BRTree,所以传的是<K,K>
我map要的是key-value模型的BRTree,所以传的是<K,pair<K,V>>
对应的BRTree传对应的模板到Node,实现不同类型的Node节点

问题2的解决:

map和set底层都是红黑树,其行为都是一致的,问题在于节点数据类型的不同,所以,红黑树的底层迭代器实现都是一样的,设置为模板,因此和问题1的解决思路是一致的。

问题3的解决:


set和map的比较不一样
set的比较是直接key,而map的比较是用kv.first
不确定是map还是set,不能写死,怎么办?
可以写一个内部类的仿函数
这个仿函数,对于set返回的是其key值
对于map返回的是其kv.first值
仿函数是一个强大的功能,你想怎么实现就怎么实现

模板写成一样的,功能是一样的,但是不同的对象类具体实现不同的功能

具体解决请看以下的泛型编程过程

二、泛型编程

1、仿函数的泛型编程


set和map的key值不一样
如何使用同一份红黑树实现不同的比较逻辑?
当对红黑树实例化时,多传一个参数,即仿函数
在红黑树底层使用一个模板仿函数
在各自的map和set写好各自的类,用于模板仿函数的实例化
这样,虽然在底层,仿函数的行为都是一致的
但是,因为模板参数不同,其返回值也就不同

set的仿函数:

	struct  SetKeyOfT
	{
		const K& operator()(const K& key)
		{
			return key;
		}

	};

map的仿函数:

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

		 };

红黑树部分的仿函数

		KeyOfT kot;
        kot(data)


2、迭代器的泛型编程

set和map有各自的数据类型
但是器迭代器的形式是一样的
如何做到?
迭代器实现,实在红黑树部分实现的
将之设置为模板
传set,就是se对应的迭代器
传map,就是map对应的迭代器

set的迭代器:

	typedef typename BRTree<const K,  K, SetKeyOfT>::Iterator iterator;
	iterator begin()
	{
		return _t.Begin();
	}

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

map的迭代器:

	typedef typename BRTree<K, pair< K, V>, MapKeyOfT>::Iterator iterator;
	iterator begin()
	{
		return _t.Begin();
	}

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

红黑树的迭代器:

//迭代器
//referrence :引用
template<class T, class Ptr, class Ref>//迭代器模板:数据类型、指针类型、解引用
struct __RBTreeiterator
{
	typedef BRTreeNode<T> Node;
	typedef __RBTreeiterator<T, Ptr, Ref> Self;//这个迭代器的对象
	Node* _node;
	
	__RBTreeiterator( Node* node)
		:_node(node)
	{
	}
	
	//解引用
	Ref operator*()
	{
		return _node->_data;
	}

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

	//比较
	bool operator!=(const Self& s)//比较的是两个迭代器对象,参数是另外一个迭代器对象
	{
		return  _node != s._node;
	}
	
	//++
	Self& operator++()
	{
		if (_node->_right)//如果右孩子不为空,找到右子树最小孩子
		{
			Node* leftMin = _node->_right;
			while (leftMin->_left)
			{
				leftMin = leftMin->_left;
			}
			_node = leftMin;

		}
		else//右孩子为空
		{
			Node* cur = _node;
			Node* parent = _node->_parent;
			while (parent && cur == parent->_right)//当孩子作为父亲的左,这个父亲就是要访问的节点
			{
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;

		}
		return *this;//this为这个对象指针

	}

};

一般的迭代器的功能:
解引用*
指针访问->
比较相等
++
--

3、typename:

在没有实例化的对象,访问其内嵌类型 
会出现分不清楚的问题:
因为静态成员、内部类、内部成员的访问都可以使用类域的方式去访问
没有实例化,就不知道访问哪一个 

在没有实例化模板的类对象去取器内嵌类型时,加一个typename
意义就是告诉编译器,等到实例化的时候再去找对应的内嵌类型

4、++/--重载

红黑树采用的是中序遍历
中序遍历,++返回的是当前节点中序遍历的下一个节点
顺序是:左,根、右
因此,需要分情况
如果当前节点有右孩子,那就是右孩子的最左节点

如果当前节点没有右孩子,那么说明自己这棵子树已经访问完毕
需要访问该子树的父亲,因为该子树是作为父亲的左孩子
按照中序遍历的思想,左子树访问完毕,接下来要访问的就是根

红黑树

#pragma once
#pragma once
#include<vector>
#include<iostream>
using namespace std;

enum Colour
{
	BLACK,
	RED
};

//node实例化,只给一个T
template<class T>
struct BRTreeNode
{
	BRTreeNode<T>* _parent;
	BRTreeNode<T>* _right;
	BRTreeNode<T>* _left;
	T _data;
	Colour _col;

	BRTreeNode(const T& data)
		:_parent(nullptr)
		, _right(nullptr)
		, _left(nullptr)
		, _data(data)
		, _col(RED)
	{
	}

};

//迭代器
//referrence :引用
template<class T, class Ptr, class Ref>//迭代器模板:数据类型、指针类型、解引用
struct __RBTreeiterator
{
	typedef BRTreeNode<T> Node;
	typedef __RBTreeiterator<T, Ptr, Ref> Self;//这个迭代器的对象
	Node* _node;
	
	__RBTreeiterator( Node* node)
		:_node(node)
	{
	}
	
	//解引用
	Ref operator*()
	{
		return _node->_data;
	}

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

	//比较
	bool operator!=(const Self& s)//比较的是两个迭代器对象,参数是另外一个迭代器对象
	{
		return  _node != s._node;
	}
	
	//++
	Self& operator++()
	{
		if (_node->_right)//如果右孩子不为空,找到右子树最小孩子
		{
			Node* leftMin = _node->_right;
			while (leftMin->_left)
			{
				leftMin = leftMin->_left;
			}
			_node = leftMin;

		}
		else//右孩子为空
		{
			Node* cur = _node;
			Node* parent = _node->_parent;
			while (parent && cur == parent->_right)//当孩子作为父亲的左,这个父亲就是要访问的节点
			{
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;

		}
		return *this;//this为这个对象指针

	}

};



template<class K, class T, class KeyOfT>
class BRTree
{

	typedef BRTreeNode<T> Node;
public:
	typedef  __RBTreeiterator<T, T*, T&>Iterator;
	//提供迭代器接口
	Iterator Begin()
	{
		Node* leftMin = _root;
		while (leftMin && leftMin->_left)//如果为空,直接返回
		{
			leftMin = leftMin->_left;
		}
		//返回一个迭代器
		//return leftMin;单参数的构造函数支持隐式类型转换
		return Iterator(leftMin);

	}

	Iterator End()
	{
		//遍历,要访问到最大的值为止
		//一般end位置为空
		return Iterator(nullptr);
	}


	bool Insert(const T& data)
	{
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			return true;
		}

		KeyOfT kot;
		Node* cur = _root;
		Node* parent = nullptr;

		while (cur)
		{
			//现在,不知道是k还是k-v模型
			//set访问的直接是key,而map访问的.first
			//所以,对应不同的返回值,仿函数解决
			if (kot(data) < kot(cur->_data))
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (kot(data) > kot(cur->_data))
			{
				parent = cur;
				cur = cur->_right;
			}
			else//找到相等key
			{
				return false;
			}
		}

		cur = new Node(data);
		cur->_col = RED;
		if (kot(data) < kot(parent->_data))//插入左
		{
			parent->_left = cur;
		}
		else //插入右
		{
			parent->_right = cur;
		}
		cur->_parent = parent;

		//插入之后,要进行颜色调整
		while (parent && parent->_col == RED)//如果为空/黑色节点,直接结束
		{

			//
			Node* grandfather = parent->_parent;

			if (parent == grandfather->_left)//p为左,u为右
			{
				Node* uncle = grandfather->_right;
				//如果叔叔存在,且为红色
				if (uncle && uncle->_col == RED)
				{
					//修改颜色
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					//向上更新
					cur = grandfather;
					parent = cur->_parent;

				}
				else//叔叔不存在/叔叔存在且为黑色
				{
					if (cur == parent->_left)
					{
						//		   g
						//	   p      u
						//  c
						//
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//		   g
						//	   p      u
						//      c
						//
						RotateL(parent);
						//		   g
						//	   c      u
						//  p
						//
						RotateR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}

			}
			else//p为右,u为左
			{
				Node* uncle = grandfather->_left;
				//如果叔叔存在,且为红色
				if (uncle && uncle->_col == RED)
				{
					//修改颜色
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					//向上更新
					cur = grandfather;
					parent = cur->_parent;

				}
				else//叔叔不存在/叔叔存在且为黑色
				{
					if (cur == parent->_right)
					{
						//		   g
						//	   u      p
						//					c
						//
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//		   g
						//	   u      p
						//          c
						//
						RotateR(parent);
						//		   g
						//	   u      c
						//  				p
						//
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
		}

		_root->_col = BLACK;

		return true;
	}

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

		parent->_left = subLR;
		if (subLR)//subLR可能为空
		{
			subLR->_parent = parent;
		}

		subL->_right = parent;
		Node* ppNode = parent->_parent;
		parent->_parent = subL;

		//注意修改顺序
		if (parent == _root)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = subL;
			}
			else
			{
				ppNode->_right = subL;
			}
			subL->_parent = ppNode;
		}

	}

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

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

		subR->_left = parent;

		Node* ppNode = parent->_parent;
		parent->_parent = subR;

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

	}

	//检查平衡
	bool isBalance()
	{
		if (_root->_col == RED)
		{
			return false;
		}

		//找到任意一条路黑色节点个数
		Node* cur = _root;
		int refNum = 0;
		while (cur)
		{
			if (cur->_col == BLACK)
			{
				refNum++;
			}
			cur = cur->_left;
		}
		return Check(_root, 0, refNum);
		return 1;
	}


	void Inoder()
	{
		_Inoder(_root);
		cout << endl;
	}

private:

	bool Check(Node* root, int blackNum, const int refNum)
	{
		//到路径结束位置检查黑色节点
		if (root == nullptr)
		{
			if (blackNum != refNum)
			{
				cout << "黑色节点不相等" << endl;
				return false;
			}
			// << blackNum << endl;
			return true;
		}

		//检查红色节点
		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout << root->_kv.first << "连续红节点" << endl;
			return false;
		}

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

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


	void _Inoder(const Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_Inoder(root->_left);
		cout << root->_kv.first << ":" << _root->_kv.second << endl;
		_Inoder(root->_right);
	}

private:
	Node* _root = nullptr;

};







map

#pragma once
#include"BRTree.h"

//对map的封装

namespace myNameSpace
{

	template<class K, class V>
	class map
	{

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

		 };

	public:
		//插入
		bool insert(const  pair <K, V>& kv)
		{
			return _t.Insert(kv);
		}

		//封装红黑树的迭代器
		typedef typename BRTree<K, pair< K, V>, MapKeyOfT>::Iterator iterator;
		iterator begin()
		{
			return _t.Begin();
		}

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

		BRTree< K, pair <K, V>, MapKeyOfT> _t;
		
	};


	void test_map()
	{
		/*map<int, int> m;
		m.insert({1,1});
		m.insert({2,2});
		m.insert({3,1});
		m.insert({7,1});


		map<int,int>::iterator it = m.begin();
		while (it != m.end())
		{
			cout << it->first << ":" << it->second << endl;
			++it;
		}
		cout << endl;*/
		map<string, int> m1;
		m1.insert({ "hello",1});
		m1.insert({ "world",2});
		m1.insert({ "find",1});
		m1.insert({ "peace",1});

		map<string, int>::iterator it1 = m1.begin();
		while (it1 != m1.end())
		{
			cout << it1->first << ":" << it1->second << endl;
			++it1;
		}
		cout << endl;

	}




}

set

#pragma once
#include"BRTree.h"

namespace myNameSpace {

	template<class K>
	class set 
	{

		struct  SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}

		};

	public:
		bool insert(const K& key)
		{
			return _t.Insert(key);
		}

		
		//对于红黑树的迭代器,需要实例化红黑树的迭代器
		//所以需要在红黑树的基础上封装迭代器
		typedef typename BRTree<const K,  K, SetKeyOfT>::Iterator iterator;
		iterator begin()
		{
			return _t.Begin();
		}

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

	private:
		BRTree<const K,K, SetKeyOfT> _t;
	};
	
	void test_set()
	{
		set<int> s;
		s.insert(1);
		s.insert(2);
		s.insert(4);
		s.insert(7);
		s.insert(8);
		s.insert(9);
		
		set<int>::iterator it = s.begin();
		while (it != s.end())
		{

			cout << *it << " ";
			++it;
		}
		cout << endl;

	}




}

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

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

相关文章

半监督的GCN:Semi-Supervised Classification With Graph Convolutional Networks

Semi-Supervised Classification With Graph Convolutional Networks -Theophilus Siameh-2017(2023) 思路 使用可扩展方法对图进行半监督学习,其中CNN应用在图数据上,得到GCN。 这种方法是在图的边的数量上进行线性的缩放模型,并学习包含局部图结构和图节点的几个隐藏层…

DiskANN数据布局

_mem.index.data&#xff1a;和sift_base.fbin一模一样。0-3字节是总向量数&#xff0c;4-7是每个向量的特征数。后面就是依次放置的每个向量。 _disk.index&#xff1a;是存储的图&#xff0c;但是不光包含图也包含原始向量。前4KB不知道存的是啥。从第0x1000开始存放的是原始…

【Python大数据】PySpark

CSDN不支持多个资源绑定&#xff0c;另外两个数据文件下载&#xff1a; 订单数据-json.zip search-log.zip Apache Spark是用于大规模数据(large-scala data)处理的统一(unified)分析引擎 简单来说&#xff0c;Spark是一款分布式的计算框架&#xff0c;用于调度成百上千的服…

【Unity之FairyGUI】你了解FGUI吗,跨平台多功能高效UI插件

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;就业…

动手学深度学习19 卷积层

动手学深度学习19 卷积层 1. 从全连接到卷积2. 卷积层3. 代码4. QA 1. 从全连接到卷积 视频&#xff1a; https://www.bilibili.com/video/BV1L64y1m7Nh/?spm_id_from333.999.0.0&vd_sourceeb04c9a33e87ceba9c9a2e5f09752ef8 3.6B元素 36亿元素–模型参数&#xff0c;存储…

JSP技术

三、JSP指令 1、page指令 在JSP页面中&#xff0c;经常需要对页面的某些特性进行描述&#xff0c;例如&#xff0c;页面的编码方式&#xff0c;JSP页面采用的语言等&#xff0c;这些特性的描述可以通过page指令实现。page指令的具体语法格式如下所示&#xff1a; <% page…

震撼发布!GPT-4o 上线!

5 月 14日凌晨一点&#xff0c;OpenAI 发布了 GPT-4o&#xff01; 新模型的功能简单概括就是&#xff1a;更快、更智能、更像人类。 秉承着持续更新的态度&#xff0c;Hulu AI 快速接入 GPT-4o 啦&#xff01; 继 5 月份上线 Suno 之后&#xff0c;这次是 Hulu AI 的又一重大…

机器学习入门介绍

各位大佬好 &#xff0c;这里是阿川的博客 &#xff0c; 祝您变得更强 个人主页&#xff1a;在线OJ的阿川 大佬的支持和鼓励&#xff0c;将是我成长路上最大的动力 阿川水平有限&#xff0c;如有错误&#xff0c;欢迎大佬指正 目录 三大方向机器学习产生的原因机器如何学习…

2024年第十届中西部外语翻译大赛

2024年第十届中西部外语翻译大赛 竞赛信息 “由中西部翻译协会共同体指导发起&#xff0c;各省市译协共建学术指导委员会&#xff0c;2024年第十届中西部外语翻译大赛由中西部翻译协会共同体秘书处&#xff08;武汉公仪网络科技有限公司&#xff09;承办。” - 获奖证书样图 -…

springSecurity快速入门

1. 介绍 springsecurity是安全框架&#xff0c;准确来说是安全管理框架。相比与另外一个安全框架Shiro&#xff0c;springsecurity提供了更丰富的功能&#xff0c;社区资源也比Shiro丰富 springsecurity框架用于Web应用的需要进行认证和授权 认证&#xff1a;验证当前访问系统…

红蓝对抗 网络安全 网络安全红蓝对抗演练

什么是红蓝对抗 在军事领域&#xff0c;演习是专指军队进行大规模的实兵演习&#xff0c;演习中通常分为红军、蓝军&#xff0c;演习多以红军守、蓝军进攻为主。类似于军事领域的红蓝军对抗&#xff0c;网络安全中&#xff0c;红蓝军对抗则是一方扮演黑客&#xff08;蓝军&…

分享四款AI论文工具和降重技术

在科研领域&#xff0c;AI写作工具如同新一代的科研利器&#xff0c;它们能够极大提高文献查阅、思路整理和表达优化的效率&#xff0c;本质上促进了科研工作的进步。AI写作工具不仅快速获取并整理海量信息&#xff0c;还帮助我们精确提炼中心思想&#xff0c;显著提升论文写作…

如何隐藏计算机IP地址,保证隐私安全?

隐藏计算机的IP地址在互联网在线活动种可以保护个人隐私&#xff0c;这是在线活动的一种常见做法&#xff0c;包括隐私问题、安全性和访问限制内容等场景。那么如何做到呢?有很5种方法分享。每种方法都有自己的优点和缺点。 1. 虚拟网络 当您连接到虚拟服务器时&#xff0c;您…

数据结构——希尔排序

懒猫老师-数据结构-(62)希尔排序_哔哩哔哩_bilibili 对直接插人的改进 基本思想 将整个待排序记录分为若干子序列&#xff0c;在子序列内分别进行直接插入排序&#xff0c;待整个序列中的记录基本有序时&#xff0c;对全体记录进行直接插入排序。 分割排序的目的 1、减少待…

DeepSpeed

文章目录 一、关于 DeepSpeed1、DeepSpeed 是什么2、深度学习训练和推理的极致速度和规模3、DeepSpeed 的四大创新支柱1&#xff09;DeepSpeed 训练2&#xff09;DeepSpeed 推理3&#xff09;DeepSpeed 压缩4&#xff09;DeepSpeed4Science 4、DeepSpeed 软件套件DeepSpeed 库推…

公共命名空间和RHP

概述 RHP的全称是&#xff1a;the little Robot that Helped me Program&#xff0c;帮我编程序的小机器人。 RHP必然存在&#xff0c;C语言的宏、C的模板&#xff0c;都是RHP&#xff1b;更复杂的例子&#xff0c;是lex和yacc&#xff0c;它们是制作程序的程序&#xff0c;也…

UE5C++ FString做为参数取值时报错error:C4840

问题描述 用来取FString类型的变量时报错&#xff1a; 问题解决 点击错误位置&#xff0c;跳转到代码&#xff1a; void AMyDelegateActor::TwoParamDelegateFunc(int32 param1, FString param2) {UE_LOG(LogTemp, Warning, TEXT("Two Param1:%d Param2:%s"), param…

Linux基本工具的使用

什么是工具&#xff1f; 在Linux中&#xff0c;工具的本质也是指令&#xff0c;只是因为这些指令与我们的开发的关系不是很大&#xff0c;所以就被称为工具 1 软件包管理器yum 在我们的Windows上如果想要安装软件&#xff0c;第一件事就是要先下载软件安装包&#xff0c;然后…

VUE之旅—day2

文章目录 Vue生命周期和生命周期的四个阶段created应用—新闻列表渲染mounted应用—进入页面搜索框就获得焦点账单统计&#xff08;Echarts可视化图表渲染&#xff09; Vue生命周期和生命周期的四个阶段 思考&#xff1a; 什么时候可以发送初始化渲染请求&#xff1f;&#xff…

Spring 各版本发布时间与区别

版本版本特性Spring Framework 1.01. 所有代码都在一个项目中 2. 支持核心功能IoC、AOP 3. 内置支持Hibernate、iBatis等第三方框架 4. 对第三方技术简单封装。如&#xff1a;JDBC、Mail、事务等 5. 只支持XML配置方式。6.主要通过 XML 配置文件来管理对象和依赖关系&#xff0…