【C++】红黑树模拟实现map和set

news2024/12/25 16:03:56

本篇基于上篇红黑树的代码来实现:

【C++】红黑树-CSDN博客

关于map和set可以看:​​​​

【C++】map和set的介绍和使用-CSDN博客


改造红黑树

map底层是红黑树的KV模型,set是红黑树的K模型,按理来说,应该设计两种红黑树来模拟实现map和set。但是STL库中并没有这样做,而是复用了同一颗红黑树

它是如何通过一颗红黑树来模拟实现map和set?

通过第二个模板参数Value来控制节点是pair类型,还是Key类型。

map中传的是pair,Value中节点就是pair类型,set中传的是Key类型,Value中节点就是Key类型。

下面我们来改造自己的红黑树:

节点不再是之前确定的pari类型,而是T类型,具体是什么类型,传给T的是pair就是

pair,传给T的是Key就是Key类型。

但是随之而来就会引发一些问题,在插入的时候就会出问题。

之前是比key小往左边走,比key大往右边走。但是现在节点data的类型并不知道。

如果此时,传给节点data的类型为Key,那么可以直接比较不会有问题,如果传给节点data的类型为pari,可以直接比较吗?

STL库中对于pair的比较是这样实现的:

解释:

first小或者first不小second小,它就小。也就是first和second有一个小就小。

这样的比较方式并不是我们想要的比较。我们想要比较只是比较first,跟second并没有关系。

所以针对map和set要设计两种不同的比较方式。

通过仿函数来设计。

如果KOfT是MapKeyOfT,就去调用map的operator()取出pair中的key,如果KOfT是SetKeyOfT,就去调用set的operator()取出key。

红黑树的迭代器

template<class T>
struct __TreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __TreeIterator<T> Self;
	Node* _node;
	__TreeIterator(Node* node)
		:_node(node)
	{	}
	T& operator*()
	{
		return _node->_data;
	}
	T* operator->()
	{
		return &_node->_data;
	}
	Self& operator++()
	{
		//
	}
	Self& operator--()
	{
		//
	}
	bool operator!=(const Self& s)
	{
		return _node != s._node;
	}
    bool operator==(const Self& s)
    {
  	    return _node == s._node;
    }
};
template<class K, class T, class KOfT>
class RBTree
{
	typedef RBTreeNode<T> Node;
public:
	typedef __TreeIterator<T> iterator;
	iterator begin()
	{
		Node* cur = _root;
		while (cur && cur->_left)
		{
			cur = cur->_left;
		}
		return iterator(cur);
	}
	iterator end()
	{
		return iterator(nullptr);
	}
    //...
    //...
namespace zxa
{
	template<class K,class V>
	class map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename RBTree<K, pair<K, V>, MapKeyOfT>::iterator iterator;
		iterator begin()
		{
			return _t.begin();
		}
		iterator end()
		{
			return _t.end();
		}
		bool Insert(const pair<K, V>& kv)
		{
			return _t.Insert(kv);
		}
	private:
		RBTree<K, pair<K, V>, MapKeyOfT> _t;
	};
	void test_map();
}
namespace zxa
{
	template<class K>
	class set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& k)
			{
				return k;
			}
		};
	public:
		typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator;
		
		iterator begin()
		{
			return _t.begin();
		}
		iterator end()
		{
			return _t.end();
		}

		bool Insert(const K& k)
		{
			return _t.Insert(k);
		}
	private:
		RBTree<K, K, SetKeyOfT> _t;
	};
	void test_set();
}

map和set只是对红黑树进行了一层封装 。

const迭代器

通过模板参数简单控制一下即可。

template<class K, class T, class KOfT>
class RBTree
{
	typedef RBTreeNode<T> Node;
public:
	typedef __TreeIterator<T,T&,T*> iterator;
	typedef __TreeIterator<T, const T&, const T*> const_iterator;
template<class T,class Ref,class Ptr>
struct __TreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __TreeIterator<T,Ref,Ptr> Self;
	Node* _node;
	__TreeIterator(Node* node)
		:_node(node)
	{	}
	Ref operator*()
	{
		return _node->_data;
	}
	Ptr operator->()
	{
		return &_node->_data;
	}
    

operator++

节点5++为节点6,节点6++为节点7,节点7++为节点8,节点8++为节点10。

  1. 如果_node的右子树不为空,中序遍历的下一个节点就是右子树的最左节点。
  2. 如果_node的右子树为空,说明_node的子树已经访问完成,下一个节点到它的祖先中找。沿着路径往上,找孩子是它的左的那个祖先。

代码实现:

Self& operator++()
{
	//1.如果右子树不为空,中序的下一个节点就是右子树的最左节点
	if (_node->_right)
	{
		Node* cur = _node;
		Node* subLeft = cur->_right;
		while (subLeft->_left)
		{
			subLeft = subLeft->_left;
		}
		_node = subLeft;
	}
	//2.如果右子树为空,说明_node所在子树已经访问完成,下一个节点到它的祖先中找。
	//沿着路径往上找 孩子是它的左的那个祖先
	else
	{
		Node* cur = _node;
		Node* parent = cur->_parent;
		while (parent && parent->_right == cur)
		{
			cur = cur->_parent;
			parent = parent->_parent;
		}
		_node = parent;
	}
	return *this;
}

operator[ ]

map的operator是调用Insert来实现的。

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

 operator[]是去找key对应的value。

V& operator[](const K& key)
{
	pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));
	return ret.first->second;
}

 关于operator[]的详细分析可以看【C++】map和set的介绍和使用-CSDN博客。

map和set的模拟及测试代码

Myset.hpp

#pragma once 

namespace zxa
{
	template<class K>
	class set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& k)
			{
				return k;
			}
		};
	public:
		typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator;
		
		iterator begin()
		{
			return _t.begin();
		}
		iterator end()
		{
			return _t.end();
		}

		pair<iterator,bool> Insert(const K& k)
		{
			return _t.Insert(k);
		}
	private:
		RBTree<K, K, SetKeyOfT> _t;
	};
	void test_set();
}

Mymap.hpp

#pragma once

namespace zxa
{
	template<class K,class V>
	class map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename RBTree<K, pair<K, V>, MapKeyOfT>::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);
		}
		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));
			return ret.first->second;
		}
	private:
		RBTree<K, pair<K, V>, MapKeyOfT> _t;
	};
	void test_map();
}

RBTree.hpp

#pragma once
#include<iostream>

using namespace std;

enum Colour
{
	BLACK,
	RED,
};
template<class T>
struct RBTreeNode
{
	RBTreeNode* _left;
	RBTreeNode* _right;
	RBTreeNode* _parent;
	T _data;

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

template<class T,class Ref,class Ptr>
struct __TreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __TreeIterator<T,Ref,Ptr> Self;
	Node* _node;
	__TreeIterator(Node* node)
		:_node(node)
	{	}
	Ref operator*()
	{
		return _node->_data;
	}
	Ptr operator->()
	{
		return &_node->_data;
	}
	Self& operator++()
	{
		//1.如果右子树不为空,中序的下一个节点就是右子树的最左节点
		if (_node->_right)
		{
			Node* cur = _node;
			Node* subLeft = cur->_right;
			while (subLeft->_left)
			{
				subLeft = subLeft->_left;
			}
			_node = subLeft;
		}
		//2.如果右子树为空,说明_node所在子树已经访问完成,下一个节点到它的祖先中找。
		//沿着路径往上找 孩子是它的左的那个祖先
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && parent->_right == cur)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}
	Self& operator--()
	{
		//...
		return *this;
	}
	bool operator!=(const Self& s)
	{
		return _node != s._node;
	}
	bool operator==(const Self& s)
	{
		return _node == s._node;
	}
};

template<class K, class T, class KOfT>
class RBTree
{
	typedef RBTreeNode<T> Node;
public:
	typedef __TreeIterator<T,T&,T*> iterator;
	typedef __TreeIterator<T, const T&, const T*> const_iterator;
	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 T& data)
	{
		//按搜索树的方式进行插入
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			//return pair<iterator(_root), true>;
			return make_pair(iterator(_root), true);
		}
		Node* parent = nullptr;
		Node* cur = _root;
		KOfT koft;
		while (cur)
		{
			if (koft(cur->_data) < koft(data))
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (koft(cur->_data) > koft(data))
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return make_pair(iterator(cur), false);
			}
		}
		cur = new Node(data);
		Node* newnode = cur;
		if (koft(parent->_data) > koft(data))
		{
			parent->_left = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
		//新增节点为红色
		cur->_col = RED;
		while (parent && parent->_col == RED)
		{
			//红黑树的调节关键看叔叔
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
				Node* uncle = grandfather->_right;
				//情况一:uncle存在且为红
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					//继续往上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					//情况三:单旋变为情况二
					if (cur == parent->_right)
					{
						RotateL(parent);
						swap(parent, cur);
					}
					//情况二:有可能为情况三变化而来
					RotateR(grandfather);
					grandfather->_col = RED;
					parent->_col = BLACK;
					break;
				}
			}
			else
			{
				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->_left)
					{
						RotateR(parent);
						swap(parent, cur);
					}
					RotateL(grandfather);
					grandfather->_col = RED;
					parent->_col = BLACK;
					break;
				}
			}
		}
		//永远把根置为黑
		_root->_col = BLACK;
		return make_pair(iterator(cur), true);
	}
	//左单旋
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		parent->_right = subRL;
		if (subRL)
		{
			subRL->_parent = parent;
		}
		subR->_left = parent;
		Node* pparent = parent->_parent;
		parent->_parent = subR;
		if (_root == parent)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			if (pparent->_left == parent)
			{
				pparent->_left = subR;
			}
			else
			{
				pparent->_right = subR;
			}
			subR->_parent = pparent;
		}
	}
	//右单旋
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		parent->_left = subLR;
		if (subLR)
		{
			subLR->_parent = parent;
		}
		subL->_right = parent;
		Node* pparent = parent->_parent;
		parent->_parent = subL;
		if (_root == parent)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (pparent->_left == parent)
			{
				pparent->_left = subL;
			}
			else
			{
				pparent->_right = subL;
			}
			subL->_parent = pparent;
		}
	}
	iterator Find(const K& key)
	{
		KOfT koft;
		Node* cur = _root;
		while (cur)
		{
			if (koft(cur->_data) < data)
			{
				cur = cur->_right;
			}
			else if (koft(cur->_data) > data)
			{
				cur = cur->_left;
			}
			else
			{
				return iterator(cur);
			}
		}
		return iterator(nullptr);
	}
private:
	Node* _root = nullptr;
};

Test.cpp

#define _CRT_SECURE_NO_WARNINGS 

#include"RBTree.hpp"
#include"Mymap.hpp"
#include"Myset.hpp"

//查找水果出现的次数
void zxa::test_map()
{
	string strs[] = { "西瓜","苹果","西瓜","西瓜","西瓜","西瓜","苹果","樱桃" };
	map<string, int> countMap;
	//1、如果水果在map中,则operator[]会插入pair<str,0>,返回映射对象(次数)的引用,对它进行++。
	//2、如果水果不在map中,则operator[]会返回映射对象(次数)的引用,对它进行++。
	for (auto& str : strs)
	{
		countMap[str]++;
	}
	for (auto& e : countMap)
	{
		cout << e.first << ":" << e.second << endl;
	}
	cout << endl;
}

void zxa::test_set()
{
	set<int> s;
	s.Insert(1);
	s.Insert(3);
	s.Insert(4);
	s.Insert(5);
	s.Insert(2);

	set<int>::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it << " " << endl;
		++it;
	}
}
int main()
{	
	zxa::test_map();
	zxa::test_set();
	return 0;
}

运行结果

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

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

相关文章

企业培训平台开发指南:基于在线教育系统源码的实现路径解析

本篇文章&#xff0c;小编将通过对在线教育系统源码的解读&#xff0c;深入探讨企业培训平台的开发路径&#xff0c;帮助企业高效构建适合自身需求的培训系统。 一、企业培训平台的需求分析 在开发企业培训平台之前&#xff0c;首先要对企业的实际需求进行充分分析。每个企业…

各种开发编程软件的下载方法--visio,navicat,pycharm,matlab等

各类开发编程类软件的下载方法 一、需要付费的 之前在网络上有很多显示可以免费下载的软件&#xff0c;不是各种在解压时需要密码的&#xff0c;就是有各种病毒的&#xff0c;绕一圈可能还得收费。 最早之前用的是 “A软件安装管家” 这个公众号里的&#xff0c;后来停更了&…

【优选算法篇】双指针的华丽探戈:深入C++算法殿堂的优雅追寻

文章目录 C 双指针详解&#xff1a;进阶题解与思维分析前言第一章&#xff1a;有效三角形的个数1.1 有效三角形的个数示例 1&#xff1a;示例 2&#xff1a;解法一&#xff08;暴力求解&#xff09;解法二&#xff08;排序 双指针&#xff09;易错点提示代码解读 第二章&#…

C++的魔法世界:类和对象的终章

文章目录 一、再探构造函数二、类型转换2.1隐式类型转换2.2内置类型的类型转化2.3explicit关键字2.4多参数构造 三、static成员四、友元五、内部类内部类的特性 六、匿名对象 一、再探构造函数 类和对象(中)里介绍的构造函数&#xff0c;使用的是赋值实现成员变量的初始化。而…

【word】文章里的表格边框是双杠

日常小伙伴们遇到word里插入的表格&#xff0c;边框是双杠的&#xff0c;直接在边框和底纹里修改边框的样式就可以&#xff0c;但我今天遇到的这个有点特殊&#xff0c;先看看表格在word里的样式是怎么样&#xff0c;然后我们聊聊如何解决。 这个双杠不是边框和底纹的设置原因…

亚洲 Web3 市场:Q3 监管变化与市场驱动力探析

概述&#xff1a; 亚洲的 Web3 市场在2024年第三季度继续表现出强劲增长势头。得益于技术精通的人口基础、政府的积极政策导向和企业的大规模参与&#xff0c;韩国、日本、越南等国家已然走在行业前沿。此外&#xff0c;随着越来越多的监管框架落地&#xff0c;区块链创新不断…

Ubuntu20.04下安装多CUDA版本,以及后续切换卸载

本方案的前提是假设机子上已经有一个版本的cuda&#xff0c;现在需要支持新的torch2.1.2和torchvision0.16.2&#xff0c;于是来安装新的cuda 一、选择版本 如果我想安装支持torch2.1.2的cuda版本&#xff0c;到官网&#xff08;https://pytorch.org/get-started/previous-ve…

【Python文件操作】掌握文件读写和目录管理的技巧!

【Python文件操作】掌握文件读写和目录管理的技巧&#xff01; 在现代编程中&#xff0c;文件操作是不可避免的一部分&#xff0c;尤其是在处理数据、日志、配置文件等场景下。Python 提供了强大而简洁的文件操作方法&#xff0c;可以轻松完成文件的读取、写入和目录管理等操作…

005_django基于Python的乡村居民信息管理系统设计与实现2024_106f2qg9

目录 系统展示 开发背景 代码实现 项目案例 获取源码 博主介绍&#xff1a;CodeMentor毕业设计领航者、全网关注者30W群落&#xff0c;InfoQ特邀专栏作家、技术博客领航者、InfoQ新星培育计划导师、Web开发领域杰出贡献者&#xff0c;博客领航之星、开发者头条/腾讯云/AW…

SpringMVC源码-异常处理机制

定义一个异常处理类TestErrorController: Controller public class TestErrorController {RequestMapping("/exception")public ModelAndView exception(ModelAndView view) throws ClassNotFoundException {view.setViewName("index");throw new ClassNot…

Mysql主从集群搭建+分库分表+ShardingSphere(实战)

什么是 ShardingSphere 介绍 Apache ShardingSphere 是一款分布式的数据库生态系统&#xff0c; 可以将任意数据库转换为分布式数据库&#xff0c;并通过数据分片、弹性伸缩、加密等能力对原有数据库进行增强。 Apache ShardingSphere 设计哲学为 Database Plus&#xff0c;…

CRMEB标准版Mysql修改sql_mode

数据库配置 1.宝塔控制面板-软件商店-MySql-设置 2.点击配置修改&#xff0c;查找sql-mode或sql_mode &#xff08;可使用CtrlF快捷查找&#xff09; 3.复制 NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 然后替换粘贴&#xff0c;保存 注&#xff1a;MySQL8.0版本的 第三步用…

Redis --- 第四讲 --- 常用数据结构 --- string类型

一、认识数据类型和编码方式 有序集合&#xff0c;相当于除了存储member之外&#xff0c;还需要存储一个score&#xff08;权重&#xff0c;分数&#xff09; Redis底层在实现上述数据结构的时候&#xff0c;会在源码层面&#xff0c;针对上述实现进行特定的优化&#xff0c;来…

文生图:Stable Diffusion、Midjourny

前言 Stable Diffusion&#xff08;SD&#xff09;和Midjourney&#xff08;MJ&#xff09;是当前流行的两款AI图像生成工具&#xff0c;它们各有特点和优势&#xff1a; **- Stable Diffusion是完全开源的&#xff0c;**这意味着用户可以免费使用&#xff0c;并且有技术能力…

excel如何把年龄转换为日期

总体的思路 我们从一个核心的日期函数出发 我们首先需要年月日 我的数据大概是这样的。 获取年份 第一步&#xff1a;提取岁前面的数字 left(目标单元格&#xff0c;“从左到右获取第几个字符”)第二步:替换掉数字后面的岁 第三步:新增一个单元格 在里面填入年 第四步:用…

Android系統Audio hal

一.Android系統Audio hal简介 Android系统的音频硬件抽象层(HAL)是系统与硬件之间的桥梁,允许音频应用和服务访问底层音频硬件,而无需直接与硬件交互。 主要组件: 音频 HAL 接口:定义了应用和服务如何调用音频硬件的规范。典型的音频操作包括播放、录制、音量控制等。 …

N1060A 50/85GHz精密型波形分析模块

N1060A 50/85GHz精密型波形分析模块 苏州新利通 概述 Keysight N1060A 精密型波形分析仪是一款数字通信分析仪&#xff08;DCA&#xff09;模块&#xff0c;可与 Keysight N1000A 主机兼容。 与是德科技的所有其他 DCA 模块一样&#xff0c;N1060A 提供了广泛的配置和性能选…

【C语言】数组函数冒泡排序bubble sort

数组&#xff1a;对于n个数字进行排序&#xff0c;就必须定义n个变量来存储。那么为了统一处理&#xff0c;选择数组就十分便捷了。 函数&#xff1a;将排序算法写到函数中&#xff0c;后续遇到所有的排序需求&#xff0c;都可以直接进行调用。 冒泡排序&#xff1a;受气泡在水…

HDFS详细分析

目录 一、HDFS架构 &#xff08;1&#xff09;Block - 数据块 &#xff08;2&#xff09;MetaData - 元数据 &#xff08;3&#xff09;NameNode - 主结点 &#xff08;4&#xff09;DataNode - 从结点 &#xff08;5&#xff09;SecondaryNameNode 二、HDFS的特点 &…

【19楼-注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…