map和set底层实现【C++】

news/2024/7/3 18:22:28

文章目录

  • map和set模板参数
  • 红黑树结点中的数据
  • 模板参数中的仿函数
  • 正向迭代器
    • ++运算符重载
    • --运算符重载
  • 库里的写法
  • set
  • map
  • RBTree

map和set模板参数

set是K模型的容器,而map是KV模型的容器
如何用一棵KV模型的红黑树同时实现map和set

  template<class K ,class V>
	class map
	{
	// ...
	private:
		RBTree<K, pair<const K, V>, MapKeyOfT> _t; //map,MapKeyOfT是仿函数
	};
	class set
	{
	//...
	private:
		RBTree<K,K,SetKeyOfT> _t;
	};//set,SetKeyOfT是仿函数

红黑树结点中的数据

红黑树的节点是K还是T,对于set都是一样的 ,对于map,底层红黑树就只能存储T了。底层红黑树不知道上层容器是map还是set,因此红黑树的结点当中直接存储T就行了。

当上层容器是set,结点当中存储的是键值Key;当上层容器是map,结点当中存储的就是<Key, Value>键值对。

template<class T >
struct RBTreeNode
{
	RBTreeNode(const T& data)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _data(data)
		, _col(RED)
	{

	}
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	T _data;
	Colour _col;//颜色
};

模板参数中的仿函数

	class set
	{
		//仿函数
		struct SetKeyOfT
		{
			const K & operator()( const K &key)
			{
				return key;
			}
		};
		private:
		RBTree<K,K,SetKeyOfT> _t;
	};
class map
	{
		//仿函数
		struct MapKeyOfT
		{
			 
			const K& operator()( const pair<K,V> & kv)
			{
				 return kv.first;
			}
		};
		private:
		RBTree<K, pair<const K, V>, MapKeyOfT> _t;
	};

当上层容器是set,T就是键值Key,直接用T进行比较即可
当上层容器是map,此时我们需要从<Key, Value>键值对当中取出键值Key后,再用Key值进行比较
上层容器map需要向底层红黑树提供一个仿函数,用于获取T当中的键值Key,当底层红黑树当中需要比较两个结点的键值时,就可以通过这个仿函数来获取T当中的键值了

对于底层红黑树,它并不知道上层容器是map还是set,因此当需要进行两个结点键值的比较时,底层红黑树都会通过传入的仿函数来获取键值Key,进而进行两个结点键值的比较。

因此,set容器也需要向底层红黑树传入一个仿函数,虽然这个仿函数单独看起来没什么用,但却是必不可少的。

当底层红黑树需要进行两个结点之间键值的比较时,都会通过传入的仿函数来获取相应结点的键值,然后再进行比较 例如:

//查找函数
iterator Find(const K& key)
{
	KeyOfT kot;
	Node* cur = _root;
	while (cur)
	{
		if (key < kot(cur->_data)) //key值小于该结点的值
		{
			cur = cur->_left; //在该结点的左子树当中查找
		}
		else if (key > kot(cur->_data)) //key值大于该结点的值
		{
			cur = cur->_right; //在该结点的右子树当中查找
		}
		else //找到了目标结点
		{
			return iterator(cur); //返回该结点
		}
	}
	return end(); //查找失败
}

正向迭代器

struct  _TreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef _TreeIterator<T,Ptr,Ref> Self;
	typedef _TreeIterator<T, T*, T&> iterator;
};
//构造函数
__TreeIterator(Node* node)
	:_node(node) //根据所给结点指针构造一个正向迭代器
{}

++运算符重载

1、右子树不为空,访问右树的最左节点(即为下一个节点)
2、如果当前结点的右子树为空,则++操作后应该在该结点的祖先结点中,找到孩子不在父亲右的祖先。


	Self & operator++()//前置++,返回++之后的值
	{
		//右子树不为空,访问右树的最左节点
		//左子树不为空,如何解决?
		if (_node->_right != nullptr)
		{
			//右树的最左节点(右树的最小节点)
			Node* subLeft = _node->_right;
			while (subLeft->_left != nullptr)
			{
				subLeft = subLeft->_left;
			}
			_node = subLeft;
		 }
		else//_node->_left != nulltptr
		{
			// 下一个要访问的节点,找孩子是父亲左的那个祖先节点

			Node* cur = _node;
			Node* parent = cur->_parent;

			while (parent!=nullptr)
			{
				if (parent->_left != cur)
				{
					//往上更新
					cur = parent;
					parent = parent->_parent;
				}
				else//parent->_left == cur
				{
					break;
				}
			
			}
			_node = parent;
		}
		return *this;
	}

–运算符重载

1、左子树存在 ,找当前节点的左子树中最右边的节点,就是所需要的节点
2、如果当前结点的左子树为空,则–操作后应该在该结点的祖先结点中,找到孩子不在父亲左的祖先。

	Self& operator--()//前置-- 
		//右子树 、根 、左子树(中序反过来) 
	{
		 
		// 左子树存在 
		//将当前节点的左子树中最右边的节点,就是所需要的节点
		if (_node->_left != nullptr)
		{
			Node* subRight = _node->_left;
			while (subRight && subRight->_right)
			{
				subRight = subRight->_right;
			}
			_node = subRight;
		}
		//右子树存在
		//找孩子是父亲的右的那个节点
		else//_node->_right != nullptr
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent!=nullptr)
			{
				//向上调整 
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;		
		}
		return *this;
	}

库里的写法

上述所实现的迭代器是有缺陷的,因为理论上我们对end()位置的正向迭代器进行–操作后,应该得到最后一个结点的正向迭代器,但我们实现end()时,是直接返回由nullptr构造得到的正向迭代器的,因此上述实现的代码无法完成此操作

C++SLT库当中的实现逻辑:
在这里插入图片描述
在红黑树的根结点处增加了一个头结点,
该头结点的左指针指向红黑树当中的最左结点,
右指针指向红黑树当中的最右结点,父指针指向红黑树的根结点。

在该结构下,实现begin()时,直接用头结点的左孩子构造一个正向迭代器即可,实现rbegin()时,直接用头结点的右孩子构造一个反向迭代器即可(实际是先用该结点构造一个正向迭代器,再用正向迭代器构造出反向迭代器),而实现end()和rend()时,直接用头结点构造出正向和反向迭代器即可。此后,通过对逻辑的控制,就可以实现end()进行–操作后得到最后一个结点的正向迭代器。

set

#pragma once 

#include "RBTree.h"
namespace cxq
{
	template<class K>//set只有一个模板参数
	class set
	{
		//仿函数
		struct SetKeyOfT
		{
			const K & operator()( const K &key)
			{
				return key;
			}
		};
	public:
		typedef  typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
		typedef  typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;

	
		iterator begin() const 
		{
			return _t.begin();
		}
		iterator end() const 
		{
			return _t.end();
		}
		pair<iterator, bool> insert(const K &key)
		{
			
			//return _t.insert(kv); //这样写return的是pair<RBTree::iterator , bool>,所以不能这样写

			//库里面的写法
			pair<typename RBTree<K, K, SetKeyOfT>::iterator, bool> ret = _t.Insert(key); //这个iterator是普通迭代器
			return pair<iterator, bool>(ret.first, ret.second);//这个iterator是const_iterator
		}
	private:
		RBTree<K,K,SetKeyOfT> _t;
	};
}

map

#pragma once 
#include"RBTree.h"
namespace cxq
{
  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< const K, V>, MapKeyOfT>::iterator iterator;
		typedef typename RBTree<K, pair< const K, V>, MapKeyOfT>::const_iterator  const_iterator;

		iterator begin ()
		{
			return _t.begin();
		}
		iterator end()
		{
			return _t.end();
		}
		const_iterator begin() const 
		{
			return _t.begin();
		}
		const_iterator end() const 
		{
			return _t.end();
		}

		 V &operator[] ( const  K & key )
		{
			 pair<iterator, bool > ret = insert(make_pair(key, V())); //V()-匿名对象
			 return  ret.first->second;
		}

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

	};
}

RBTree

#pragma once 
#include<iostream>
#include<assert.h>
#include<vector>
using namespace std;
enum Colour
{
	RED,
	BLACK
};

template<class T >
struct RBTreeNode
{
	RBTreeNode(const T& data)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _data(data)
		, _col(RED)
	{

	}
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	T _data;
	Colour _col;//颜色


};

template <class T,class Ptr,class Ref>
struct  _TreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef _TreeIterator<T,Ptr,Ref> Self;
	typedef _TreeIterator<T, T*, T&> iterator;

	//??
	_TreeIterator(const iterator & it)
		:_node(it._node)
	{

	}


	//构造函数 
	_TreeIterator(Node* node)
		:_node(node)
	{

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

	 Ptr operator->()// T * operator->()
	{
		return &_node->_data;
	}
	bool operator!=(const Self & s)
	{
		return _node != s._node;
	}
	Self& operator--()//前置-- 
		//右子树 、根 、左子树(中序反过来) 
	{
		 
		// 左子树存在 
		//将当前节点的左子树中最右边的节点,就是所需要的节点
		if (_node->_left != nullptr)
		{
			Node* subRight = _node->_left;
			while (subRight && subRight->_right)
			{
				subRight = subRight->_right;
			}
			_node = subRight;
		}
		//右子树存在
		//找孩子是父亲的右的那个节点
		else//_node->_right != nullptr
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent!=nullptr)
			{
				//向上调整 
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;		
		}
		return *this;
	}
	Self & operator++()//前置++,返回++之后的值
	{
		//右子树不为空,访问右树的最左节点
		//左子树不为空,如何解决?
		if (_node->_right != nullptr)
		{
			//右树的最左节点(右树的最小节点)
			Node* subLeft = _node->_right;
			while (subLeft->_left != nullptr)
			{
				subLeft = subLeft->_left;
			}
			_node = subLeft;
		 }
		else//_node->_left != nulltptr
		{
			// 下一个要访问的节点,找孩子是父亲左的那个祖先节点

			Node* cur = _node;
			Node* parent = cur->_parent;

			while (parent!=nullptr && cur == parent->_right)
			{
				//	往上更新
				cur = cur->_parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}
	Node* _node;

};


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

public:
	//同一个类模板,传的不同参数,实例化不同的类型
	typedef _TreeIterator<T,T* ,T&> iterator;
	typedef _TreeIterator<T, const T* , const T &> const_iterator;//const的位置如何选择 
public:
	iterator begin()
	{
		//最左边即最小值节点
		Node* leftMin = _root;
		while (leftMin!=nullptr && leftMin->_left!=nullptr)
		{
			leftMin = leftMin->_left;

		}
		return iterator(leftMin);//支持单参数构造支持隐式类型转换

	}
	iterator end() 
	{
		return iterator(nullptr);//支持单参数构造支持隐式类型转换
	}


	const_iterator begin()const
	{
		//最左边即最小值节点
		Node* leftMin = _root;
		while (leftMin != nullptr && leftMin->_left != nullptr)
		{
			leftMin = leftMin->_left;

		}
		return const_iterator(leftMin);//支持单参数构造支持隐式类型转换

	}
	const_iterator end()const
	{
		return const_iterator(nullptr);//支持单参数构造支持隐式类型转换
	}
	Node * Find(const K & key)
	{
		Node* cur = _root;
		KeyOfT kot;
		while (cur!= nullptr)
		{	
			if (kot(cur->_data) < key)
			{
				cur = cur->_left;
			}
			else if (kot(cur->_data) > key)
			{
				cur = cur->_right;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}
	pair<iterator, bool> Insert(const T & data)
	{
		//找到插入位置

		//空树
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			return make_pair(iterator(_root), true);
		}
		//不是空树 
		Node* cur = _root;
		Node* parent = nullptr;
		KeyOfT kot;

		while (cur != nullptr)
		{
			//用仿函数比较 
			if (kot(cur->_data	) > kot(data ))
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (kot(cur->_data)  < kot(data) )
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return make_pair(  iterator(cur) ,false);
			}
		}


		cur = new Node(data);
		cur->_col = RED;
		Node* newnode = cur;
		//将插入节点插入到树中 
		if (kot(parent->_data) > kot(data) ) //用仿函数 
		{
			parent->_left = cur;
		}
		else//kot(parent->_data) < kot(data)
		{
			parent->_right = cur;
		}
		cur->_parent = parent;

		//如果插入节点的父节点是红色,分三种情况进行调整 
		while (parent!=nullptr &&parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			assert(grandfather);
			assert(grandfather->_col == BLACK);


			if (parent == grandfather->_left)
			{
				Node* uncle = grandfather->_right;
				//插入结点的叔叔存在,且颜色是红色
				if (uncle != nullptr && uncle->_col == RED)
				{


					//parent 、uncle  变黑
					//grandfather变红
					parent->_col = BLACK;
					uncle->_col = BLACK;
					grandfather->_col = RED;

					//向上处理 
					cur = grandfather;
					parent = cur->_parent;
				}
				//插入结点的叔叔存在,且颜色是黑色
				//插入节点的叔叔不存在
				//这两种情况统一处理
				else  if (uncle != nullptr && uncle->_col == BLACK || uncle == nullptr)
				{
					//先旋转再变色

					if (cur == parent->_left)
					{
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else//cur == parent->_right 
					{
						RotateL(parent);
						RotateR(grandfather);

						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}

			}
			else//parent == grandfather->_right
			{
				Node* uncle = grandfather->_left;
				//插入结点的叔叔存在,且颜色是红色
				if (uncle != nullptr && uncle->_col == RED)
				{


					//parent 、uncle  变黑
					//grandfather变红
					parent->_col = BLACK;
					uncle->_col = BLACK;
					grandfather->_col = RED;

					//向上处理 
					cur = grandfather;
					parent = cur->_parent;
				}
				//插入结点的叔叔存在,且颜色是黑色
				//插入节点的叔叔不存在
				//这两种情况统一处理
				else  if (uncle != nullptr && uncle->_col == BLACK || uncle == nullptr)
				{
					//先旋转再变色

					if (cur == parent->_left)
					{
						//双旋	
						
						RotateR(parent);
						RotateL(grandfather); 


						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					else//cur == parent->_right 
					{
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}

			}
		}



		_root->_col = BLACK;
		return make_pair(iterator(newnode),true);
	}
	void RotateL(Node* parent)
	{
		_rotateCount++;
		Node* cur = parent->_right;

		Node* curleft = cur->_left;

		//parent和curleft链接
		parent->_right = curleft;
		if (curleft != nullptr)
		{
			
			curleft->_parent = parent;
		}
		//cur和parent链接
		cur->_left = parent;

	
		Node* ppnode = parent->_parent;
		parent->_parent = cur;

		//parent 是根节点
		if (parent == _root)
		{
			cur->_parent = nullptr;
			_root = cur;
		}
		else//parent 是一个子树 
		{
			//parent是一个左子树
			

		
			if (ppnode->_left == parent)
			{
				//3和ppnode链接
				ppnode->_left = cur;
				cur->_parent = ppnode;
			}
			//parent是一个右子树
			else
			{
				//3和ppnode链接
				ppnode->_right = cur;
				cur->_parent = ppnode;
			}
		}

	}
	void RotateR(Node* parent)
	{
		_rotateCount++;
		Node* cur = parent->_left;
		Node* curright = cur->_right;

		//cur和parent 链接 

		cur->_right = parent;
		Node* ppnode = parent->_parent;
		parent->_parent = cur;

		//curright和parent链接 
		parent->_left = curright;
		if (curright != nullptr)
		{
			curright->_parent = parent;
		}



		
		if (parent == _root)//parent是根节点
		{
			cur->_parent = nullptr;
			_root = cur;//更新根节点
		}
		else//parent一个子树 
		{
			//parent是左子树 
			if (ppnode->_left == parent)
			{
				ppnode->_left = cur;
				cur->_parent = ppnode;
			}
			else//(ppnode->_right == parent)
				//parent是右子树 
			{
				ppnode->_right = cur;
				cur->_parent = ppnode;
			}
		}


	}

	bool CheckColour(Node* root, int blacknum, int benchMark)
	{
		if (root == nullptr)
		{
			//blacknum是一条路径上的黑色节点总数 
			if (blacknum != benchMark)
			{
				return  false;
			}
			return false;
		}
		else if (root->_col == BLACK)
		{
			blacknum++;

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

		return   CheckColour(root->_left, blacknum, benchMark)
			&& CheckColour(root->_right, blacknum, benchMark);
	}
	bool IsBalance()
	{
	  return 	_IsBalance(_root);
	}

	bool _IsBalance(Node* root)
	{
		if (root == nullptr)
		{
			return  true;
		}
		//根节点必须是黑色
		if (root->_col != BLACK)
		{
			return false;
		}
		//基准值
		//求最左路径的黑色节点
		int benchMark = 0;
		Node* cur = _root;
		while (cur != nullptr)
		{
			if (cur->_col == BLACK)
			{
				benchMark++;
			}
			cur = cur->_left;
		}
		return CheckColour(root, 0, benchMark);
	}
	int Height()
	{
		return _Height(_root);
	}
    int _Height(Node *root)
	{
		if (root == nullptr)
		{
			return 0;
		}
		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);
		return  leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}
private:
	Node* _root = nullptr;
public:
	int _rotateCount = 0;
};

http://lihuaxi.xjx100.cn/news/1698424.html

相关文章

FPGA片上RAM、片上ROM Nios 程序不起作用的解决方法

ctrl B 编译Nios工程 将 Nios software 的 meminit.qip onchip_rom.hex onchip_ram.hex meminit.spd 文件拷贝到FPGA目录下&#xff0c;再编译FPGA能起作用 Nios设置&#xff1a; reset 设置为 ROM 异常设置为 RAM 无优化

直接在网页上获取【微信公众号】用户的【openId】

一、公众号openId是什么&#xff1f; 公众号openId是用户在当前公众号下的唯一标识&#xff08;‘身份证’&#xff09;&#xff0c;就是说通过这个openId&#xff0c;就能区分在这个公众号下具体是哪个用户。 获取微信公众号用户的OpenId&#xff08;用户关注公众号后&#x…

基于JAVA+SpringBoot+UniApp+Vue的前后端分离的手机移动端图书借阅平台

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 随着社会信息化的快速…

Python调用包,及路径问题

我震惊了&#xff0c;搜了很多博客没有理解好的问题&#xff0c;GPT可以理解我的问题&#xff0c;并做出回答。也许这才是人工智能解决问题的意义&#xff0c;感谢GPT。 文章目录 问题1: 如何在自己的Python项目中调用自己写的文件问题2&#xff1a;如果执行调用的文件和被调用…

全志R128软件配置——RTOS 软件包配置

RTOS 软件包配置 本文将介绍 RTOS 软件包、地址&#xff0c;内核配置等。 Kconfig 简介 有过 linux 内核开发经验的人&#xff0c;对 menuconfig 不会陌生。对于各类内核&#xff0c;只要是支持 menuconfig 配置界面&#xff0c;都是使用 Kconfig。 换言之&#xff1a; me…

vue3+ts 使用vue-print-nb 打印功能【完美】

安装 npm i vue3-print-nb -s我的版本 0.1.4 注册 import { createApp } from vue import pinia from ./store import router from ./router import ./assets/css/main.scss import { globalRegister } from /global/register import ant-design-vue/es/message/style/css …

“In Global,For China”,许战海咨询LOGO全面焕新

许战海咨询LOGO全面焕新&#xff0c;正式开启全球化战略! 作为一家起源于中国的全球性战略咨询公司&#xff0c;全新LOGO展现许战海咨询国际化企业形象&#xff0c;诠释出许战海咨询最新、最前沿的战略咨询理念“In Global&#xff0c;For China”(在全球&#xff0c;为中国)。…

高级软件工程师职业发展路径及适学人群

大数据赋能产业&#xff0c;改变人与场景的交互方式&#xff0c;随着大数据在各行各业融合应用的不断深化&#xff0c;预计2020年中国大数据市场市值将突破10000亿元。 未来发展&#xff0c;大数据应用范围越加宽广&#xff0c;包括&#xff1a;新零售、旅游、广告、电商…