【C++】Map、Set 模拟实现

news2024/12/24 8:25:29

文章目录

  • 📕 概念
  • 📕 实现
    • 框架
    • Find()
    • ★ 迭代器 ★
    • 反向迭代器
    • map 的 operator[ ]
  • 📕 源代码
    • rb_tree.h
    • set.h
    • map.h

📕 概念

map、set 是 C++ 中的关联式容器,由于 map 和set所开放的各种操作接口,RB-tree 也都提供了,所以几乎所有的 map 和set的操作行为,都只是转调 红黑树 的操作行为。

关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高。

📕 实现

框架

map、set 一个很明显的区别就是,set 存储值,map存储键值对

set 的结构如下,只需要传入一个模板参数 K ,也就是其实际存储的值的数据类型。复用红黑树的时候,第二个参数就是 K 类型。

#pragma once
#include"rb_tree.h"

namespace SetSimulate
{
	template<class K>
	class set
	{
		struct KeyOfT
		{
			const K& operator()(const K& k)
			{
				return k;
			}
		};

	public:

		typedef typename RBTree<K, K, KeyOfT>::const_iterator iterator;
		typedef typename RBTree<K, K, KeyOfT>::const_iterator const_iterator;
		

	private:
		RBTree<K, K, KeyOfT> _t;
	};
}

而 Map 就不一样了,要传入两个参数,第一个是 K(键值),第二个是 V (映射值)。实际存储的数据类型是 pair<K,V> 。如下,复用红黑树的时候,将其第二个模板参数设为 pair<K,V> 即可。

#pragma once
#include"rb_tree.h"


namespace MapSimulate
{
	template<class K, class V>
	class map
	{
		struct MapOfKey
		{
			const K& operator()(const pair<const K, V>& kv)
			{
				return kv.first;
			}
		};

	public:
		typedef typename RBTree<K, pair<const K,V>, MapOfKey>::iterator iterator;
		typedef typename RBTree<K, pair<const K,V>, MapOfKey>::const_iterator const_iterator;
		typedef typename RBTree<K, pair<const K, V>, MapOfKey>::reverse_iterator reverse_iterator;
		typedef typename RBTree<K, pair<const K, V>, MapOfKey>::const_reverse_iterator const_reverse_iterator;


	private:
		RBTree<K, pair<const K, V>, MapOfKey> _t;
	};
}

关于 map、set 底层红黑树的第三个模板参数,分析上面的两段代码,实际上是取存储数据的 K 值。即,在 map 里面,由于存储的数据类型是 pair<K,V> ,所以 MapOfKey 的作用是取到键值对里面 K 类型的数据;而 set 里面,由于存储的数据本身就是 K 类型,所以 SetOfKey 直接取到该类型的数据

至于为什么要这样,这是因为,map、set 是对红黑树的封装,如果仅仅实现 set ,可以不用给红黑树传入第三个模板参数,因为 set 里面存储 K 类型的数据,而红黑树插入、查找、删除等等操作里面,数据之间进行比较,本就是比较的 K 类型的数据,所以是一致的。
但是,map 存储的是 pair<K,V> ,由于map、set 是复用同一份红黑树代码,所以无法将代码直接改成适应 pair<K,V> 的情况(这样就不适应 set 了)。所以,需要有一个统一的视角,来得到map、set 的数据里面的 K 值。

当然,上面两端说起来比较抽象,接下来介绍一下 Find 封装,就不难理解了。

Find()

如下是红黑树底层实现 find 的代码。可以看到它在进行数据之间的比较时,是依靠 KeyOfT 实例化出的对象 kt ,取 kt(cur->_data)
很显然,当容器无论为 set 还是 map, 红黑树中 find() 函数都依靠 K 类型的 val 来查找。可是, 红黑树的节点中, _data 存储的数据类型却不一样,map 中存储的是 pair<K,V>,set 中存储的是 K。

所以,这里就依靠红黑树的第三个模板参数 KeyOfT 实例化的对象 kt,其重载的 () 来取到 map、set 的 _data 里面的 K 值。这样就统一了。

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

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


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

	public:
		Node* find(const K& val)
		{
			KeyOfT kt;
			Node* cur = _root;
			while (cur)
			{
				if (val > kt(cur->_data))
				{
					cur = cur->_right;
				}
				else if (val < kt(cur->_data))
				{
					cur = cur->_left;
				}
				else
					return cur;
			}
			return nullptr;
		}
		
	private:
		Node* _root;
	};

如下,无论是 map 还是 set,都是直接调用红黑树的 find() ,然后封装成迭代器返回。

iterator Find(const K& key)
		{
			Node* cur = _t.find(key);
			return iterator(cur);
		}

★ 迭代器 ★

如下,是迭代器的大体框架,主要依靠红黑树的节点指针来实现。

重载 * 、->、!= 都比较简单,和 list 封装的迭代器类似。但是 ,重载 ++ 和 – 就需要一些独特的思路了!

template<class T,class Ref,class Ptr>
	struct _RBTreeIterator
	{
		typedef RBTreeNode<T> Node;
		typedef _RBTreeIterator<T, Ref, Ptr> self;


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

		// begin() 、 end() 处可见其效果
		_RBTreeIterator(const _RBTreeIterator<T, T&, T*> &it)
			:_node(it._node)
		{}


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

		bool operator!=(const self& s)
		{
			return this->_node != s._node;
		}

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

		self& operator++()
		{
			// 实现
			
			return *this;
		}

		self& operator--()
		{
			// 实现
			
			return *this;
		}
		
		Node* _node;
	};

operator++()

对红黑树的遍历,和搜索二叉树是一样的,中序遍历
例如,现在某棵红黑树存储了1到10 的数据,使用迭代器遍历的结果必然是 1、2、3、4、5、6、7、8、9、10。
假设现在将存储 5 这个数据的节点,封装成迭代器 it。进行 ++it 操作之后,无疑 it 内部的节点指针,指向的是数据为 6 的节点。再 ++it,it 内部的结点指针就指向数据为 7 的节点。
很明显,++ 操作,只需要考虑右边部分的结果(上面依次++it,实际上节点指针依次指向 6、7、8、9、10 ),不需要考虑 左边的部分(即 1-5 )。

这样就很清晰了,对于一个中序遍历的结果,对于某一个节点 X 的右边部分,分为两种情况:有数据是 X 的子节点(可能是部分,可能是全部);没有数据是 X 的子节点。例如上面 5 这个结果,其右边部分6到10 里面,可能有数据是5的子节点,也可能没有。

根据这两种情况来写代码。

  • 对于第一种情况——右边部分存在 X 的子节点。这就很容易了,由于是中序遍历,当前节点遍历完之后,就需要遍历其右子树。所以 ++ 操作,只要找到其右子树的最小节点即可

  • 对于第二种情况,X 不存在右子树,那么必然是要往上找。如下图,假设一开始是图中左边的状态, 现在有 cur 、p 两个节点指针,而 cur 没有右子树,向上寻找, cur 在 p 的右边,这说明 p 已经遍历过了。
    再继续往上找,到了下图右边的状态,此时 cur 在 p 的左边,根据中序遍历规则,下一个要遍历的节点就是 此时的 p

请添加图片描述

self& operator++()
		{
			// 右子树存在,先去右子树的第一个
			if (_node->_right)
			{
				Node* subleft=_node->_right;
				while (subleft->_left)
				{
					subleft = subleft->_left;
				}

				_node = subleft;
			}
			else // 右子树不存在,向上找
			{
				Node* cur = _node;
				Node* parent = _node->_parent;
				while (parent && parent->_right == cur)
				{
					cur = cur->_parent;
					parent = parent->_parent;
				}
				_node = parent;
			}
			
			return *this;
		}

operator–()

而 – 就是和 ++ 反过来了,遍历顺序是:右子树、根、左子树。所以,拿到某个节点 X,要先去看左子树是否存在,不存在则往上找。

  • 左子树存在,自然是去寻找左子树中最大的节点
  • 左子树不存在,往上找。如下图,假设当前迭代器封装的是 cur 节点,不存在左子树。 – 操作就应该向上找,在下图左边的状态时, cur 节点是 p节点的左孩子,根据 – 的遍历顺序,p 已经遍历过了。
    继续往上,到了下图右边的状态, cur 是 p 的右孩子,根据 - - 的遍历顺序,下一个节点就是 p

请添加图片描述

self& operator--()
		{
			if (_node->_left)
			{
				Node* subright = _node->_left;
				while (subright->_right)
				{
					subright = subright->_right;
				}
				_node = subright;
			}
			else // 当前节点没有左子树,直接向上找,直到 parent 的左子节点是 cur
			{
				Node* cur = _node;
				Node* parent = cur->_parent;
				while (parent && parent->_left == cur)
				{
					cur = cur->_parent;
					parent = parent->_parent;
				}
				_node = parent;
			}

			return *this;
		}

反向迭代器

反向迭代器主要是对正向迭代器的封装。

template<class T, class Ref, class Ptr>
	struct _RBTreeReverseIterator
	{
		typedef RBTreeNode<T> Node;
		typedef _RBTreeReverseIterator<T, Ref, Ptr> self;

		_RBTreeReverseIterator(Node* node)
		{
			_it._node = node;
		}

		_RBTreeReverseIterator(const _RBTreeIterator<T, T&, T*>& it)
		{
			_it._node = it._node;
		}

		_RBTreeReverseIterator(const _RBTreeReverseIterator<T, T&, T*>& it)
		{
			_it._node = it._it._node;
		}
		
		self& operator++()
		{
			--_it;
			return *this;
		}

		self& operator--()
		{
			++_it;
			return *this;
		}

		bool operator!=(const self& s)
		{
			return _it._node != s._it._node;
		}

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

		Ref operator*()
		{
			return _it._node->_data;
		}

		struct _RBTreeIterator<T,Ref,Ptr> _it=nullptr;
	};


在红黑树中实现了 迭代器,接下来就是封装到 map、set 里面。与此同时,还需要红黑树中提供 begin() 、end() 、rbegin() 、rend() 方法。

如下,是在红黑树中实现这些方法,返回的结果是迭代器。

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

	public:
		typedef _RBTreeIterator<T, T&, T*> iterator;
		typedef _RBTreeIterator<T, const T&, const T*> const_iterator;
		typedef _RBTreeReverseIterator<T, T&, T*> reverse_iterator;
		typedef _RBTreeReverseIterator<T, const T&, const T*> const_reverse_iterator;
	public:

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

			return iterator(cur);
		}

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

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

			return const_iterator(cur);
		}

		const_iterator end() const
		{
			return const_iterator(nullptr);
		}


		reverse_iterator rbegin()
		{
			Node* cur = _root;
			while (cur && cur->_right)
			{
				cur = cur->_right;
			}

			return reverse_iterator(cur);
		}

		reverse_iterator rend()
		{
			return reverse_iterator(nullptr);
		}

		const_reverse_iterator rbegin() const
		{
			Node* cur = _root;
			while (cur && cur->_right)
			{
				cur = cur->_right;
			}

			return const_reverse_iterator(cur);
		}

		const_reverse_iterator rend() const
		{
			return const_reverse_iterator(nullptr);
		}

	private:
		Node* _root;
	};

如下是在 set 里面实现 begin() 、end() ,完全是对 红黑树方法的复用。

值得注意的是, typedef typename RBTree<K, K, KeyOfT>::const_iterator iterator; 这是为了保证 set 的 K 不可修改。

#pragma once
#include"rb_tree.h"

namespace SetSimulate
{
	template<class K>
	class set
	{
		struct KeyOfT
		{
			const K& operator()(const K& k)
			{
				return k;
			}
		};

	public:

		typedef typename RBTreeNode<K> Node;
		typedef typename RBTree<K, K, KeyOfT>::const_iterator iterator;
		typedef typename RBTree<K, K, KeyOfT>::const_iterator const_iterator;
		typedef typename RBTree<K, K, KeyOfT>::const_reverse_iterator reverse_iterator;
		typedef typename RBTree<K, K, KeyOfT>::const_reverse_iterator const_reverse_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();
		}

		reverse_iterator rbegin()
		{
			return _t.rbegin();
		}

		reverse_iterator rend()
		{
			return _t.rend();
		}

		const_reverse_iterator rbegin()const
		{
			return _t.rbegin();
		}

		const_reverse_iterator rend()const
		{
			return _t.rend();
		}

		iterator Find(const K& key)
		{
			Node* cur = _t.find(key);
			return iterator(cur);
		}

	private:
		RBTree<K, K, KeyOfT> _t;
	};
}

map 的 operator[ ]

首先,map 的 [ ] 是兼具插入和查找功能的。map 里面存储的是 pair<K,V> ,假设有一个map< int,int >对象 m,执行 m[10]=1000; —— 如果存储的 pair<K,V> 里面,有 K 类型的数据是 10,那么就将这个键值对的 V 修改成 100;没有则插入 make_pair(10,100);

要完成上面描述的功能,就必须要让 [ ] 的返回值是 V& (V的引用),这样才可以修改 V 类型的数据。 可以通过迭代器来实现!!

如下是 map 里面实现 operator[ ] 的代码,_t 是红黑树实例化出来的对象,其 Insert() 返回值是 pair<iterator,bool> , 很明显,返回值包含两个信息:

  1. 某个节点构成的迭代器。如果插入成功,那么返回插入节点封装成的迭代器。如果插入失败(已经有了 K 类型对应的数据),那么就相当于查找功能,返回找到的该节点封装成的迭代器
  2. 是否插入成功,插入成功返回 true,否则false。
		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));
			return ret.first->second;
		}

那么就可以对红黑树底层的 Insert() 进行修改,只需要修改函数的返回值类型,以及 return 的时候,构造一个 make_pair<iterator,bool> 类型的数据进行返回即可。

📕 源代码

rb_tree.h

#pragma once
#include<iostream>
#include<cassert>

using namespace std;


	enum Colour
	{
		RED,
		BLACK,
	};

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

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

	

	template<class T,class Ref,class Ptr>
	struct _RBTreeIterator
	{
		typedef RBTreeNode<T> Node;
		typedef _RBTreeIterator<T, Ref, Ptr> self;


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


		_RBTreeIterator(const _RBTreeIterator<T, T&, T*> &it)
			:_node(it._node)
		{}


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

		bool operator!=(const self& s)
		{
			return this->_node != s._node;
		}

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

		self& operator++()
		{
			// 右子树存在,先去右子树的第一个
			if (_node->_right)
			{
				Node* subleft=_node->_right;
				while (subleft->_left)
				{
					subleft = subleft->_left;
				}

				_node = subleft;
			}
			else // 右子树不存在,向上找
			{
				Node* cur = _node;
				Node* parent = _node->_parent;
				while (parent && parent->_right == cur)
				{
					cur = cur->_parent;
					parent = parent->_parent;
				}
				_node = parent;
			}

			return *this;
		}

		self& operator--()
		{
			if (_node->_left)
			{
				Node* subright = _node->_left;
				while (subright->_right)
				{
					subright = subright->_right;
				}
				_node = subright;
			}
			else // 当前节点没有左子树,直接向上找,直到 parent 的左子节点是 cur
			{
				Node* cur = _node;
				Node* parent = cur->_parent;
				while (parent && parent->_left == cur)
				{
					cur = cur->_parent;
					parent = parent->_parent;
				}
				_node = parent;
			}

			return *this;
		}



		Node* _node;
	};


	template<class T, class Ref, class Ptr>
	struct _RBTreeReverseIterator
	{
		typedef RBTreeNode<T> Node;
		typedef _RBTreeReverseIterator<T, Ref, Ptr> self;

		_RBTreeReverseIterator(Node* node)
		{
			_it._node = node;
		}

		_RBTreeReverseIterator(const _RBTreeIterator<T, T&, T*>& it)
		{
			_it._node = it._node;
		}

		_RBTreeReverseIterator(const _RBTreeReverseIterator<T, T&, T*>& it)
		{
			_it._node = it._it._node;
		}
		
		self& operator++()
		{
			--_it;
			return *this;
		}

		self& operator--()
		{
			++_it;
			return *this;
		}

		bool operator!=(const self& s)
		{
			return _it._node != s._it._node;
		}

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

		Ref operator*()
		{
			return _it._node->_data;
		}

		struct _RBTreeIterator<T,Ref,Ptr> _it=nullptr;
	};



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

	public:
		typedef _RBTreeIterator<T, T&, T*> iterator;
		typedef _RBTreeIterator<T, const T&, const T*> const_iterator;
		typedef _RBTreeReverseIterator<T, T&, T*> reverse_iterator;
		typedef _RBTreeReverseIterator<T, const T&, const T*> const_reverse_iterator;
	public:

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

			return iterator(cur);
		}

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

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

			return const_iterator(cur);
		}

		const_iterator end() const
		{
			return const_iterator(nullptr);
		}


		reverse_iterator rbegin()
		{
			Node* cur = _root;
			while (cur && cur->_right)
			{
				cur = cur->_right;
			}

			return reverse_iterator(cur);
		}

		reverse_iterator rend()
		{
			return reverse_iterator(nullptr);
		}

		const_reverse_iterator rbegin() const
		{
			Node* cur = _root;
			while (cur && cur->_right)
			{
				cur = cur->_right;
			}

			return const_reverse_iterator(cur);
		}

		const_reverse_iterator rend() const
		{
			return const_reverse_iterator(nullptr);
		}


		~RBTree()
		{
			Delete(_root);
			_root = nullptr;
		}

		Node* find(const K& val)
		{
			KeyOfT kt;
			Node* cur = _root;
			while (cur)
			{
				if (val > kt(cur->_data))
				{
					cur = cur->_right;
				}
				else if (val < kt(cur->_data))
				{
					cur = cur->_left;
				}
				else
					return cur;
			}

			return nullptr;
		}

		pair<iterator,bool> Insert(const T& val)
		{
			if (_root == nullptr)
			{
				_root = new Node(val);
				_root->_col = BLACK;
				return make_pair(iterator(_root),true);
			}
			KeyOfT kt;
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur) // 找到要插入的位置
			{
				if (kt(cur->_data) > kt(val))
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (kt(cur->_data) < kt(val))
				{
					parent = cur;
					cur = cur->_right;
				}
				else
					return make_pair(iterator(cur), false);
			}

			// 插入
			cur = new Node(val);
			Node* newnode = cur;
			if (kt(parent->_data) < kt(val))
			{
				parent->_right = cur;
			}
			else if (kt(parent->_data) > kt(val))
			{
				parent->_left = cur;
			}
			cur->_parent = parent;


			// 调整颜色
			while (parent && parent->_col == RED)
			{
				Node* grandfather = parent->_parent;
				if (parent == grandfather->_left)
				{
					Node* uncle = grandfather->_right;
					if (uncle && uncle->_col == RED) // uncle 为红色
					{
						parent->_col = BLACK;
						uncle->_col = BLACK;
						grandfather->_col = RED;

						// 继续调整
						cur = grandfather;
						parent = cur->_parent;
					}
					else // uncle不存在 or 存在且为黑
					{
						if (cur == parent->_left)
						{
							RotateR(grandfather);
							parent->_col = BLACK;
							grandfather->_col = RED;
							//cur->_col = RED;
						}
						else if (cur == parent->_right)
						{
							RotateL(parent);
							RotateR(grandfather);
							cur->_col = BLACK;
							grandfather->_col = RED;
							parent->_col = RED;
						}

						break;
					}
				}
				else // if (parent == grandfather->_right)
				{
					Node* uncle = grandfather->_left;
					if (uncle && uncle->_col == RED) // uncle 为红色
					{
						parent->_col = BLACK;
						uncle->_col = BLACK;
						grandfather->_col = RED;

						// 继续调整
						cur = grandfather;
						parent = cur->_parent;
					}
					else // uncle不存在 or 存在且为黑
					{
						if (cur == parent->_right)
						{
							RotateL(grandfather);
							parent->_col = BLACK;
							grandfather->_col = RED;
							//cur->_col = RED;
						}
						else // cur == parent->_left
						{
							RotateR(parent);
							RotateL(grandfather);
							cur->_col = BLACK;
							parent->_col = RED;
							grandfather->_col = RED;
						}

						break;
					}
				}
			}

			_root->_col = BLACK;
			return make_pair(iterator(newnode), true);
		}

		int Height()
		{
			return _Height(_root);
		}

		bool isBalance()
		{
			if (_root->_col == RED)
			{
				cout << "根节点的颜色是红色" << endl;
				return false;
			}

			int benchmark = 0;
			Node* tmp = _root;
			while (tmp)
			{
				if (tmp->_col == BLACK)
					benchmark++;
				tmp = tmp->_left;
			}

			return _isBalance(_root, 0, benchmark);
		}

		void Inorder()
		{
			_Inorder(_root);
		}

	private:

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

			_Inorder(root->_left);
			cout << root->_kv.first << " ";
			_Inorder(root->_right);
		}

		bool _isBalance(Node* root, int blackNum, int benchmark)
		{
			if (root == nullptr)
			{
				if (blackNum == benchmark)
					return true;
				else
				{
					cout << "某条链路黑色节点数目错误!" << endl;
					return false;
				}
			}

			if (root->_col == BLACK)
				++blackNum;
			else
			{
				if (root->_parent && root->_parent->_col == RED)
				{
					cout << "存在两个连续的红色节点" << endl;
					return false;
				}
			}

			return _isBalance(root->_left, blackNum, benchmark)
				&& _isBalance(root->_right, blackNum, benchmark);
		}

		int _Height(Node* root)
		{
			if (root == nullptr) return 0;

			int leftH = _Height(root->_left);
			int rightH = _Height(root->_right);

			return leftH > rightH ? leftH + 1 : rightH + 1;
		}



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

			Delete(root->_left);
			Delete(root->_right);
			free(root);
		}

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

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

			Node* pparent = parent->_parent;
			subR->_left = parent;
			parent->_parent = subR;

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


		}



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

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

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


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

				subL->_parent = pparent;
			}

		}

	private:
		Node* _root;
	};


set.h

#pragma once
#include"rb_tree.h"

namespace SetSimulate
{
	template<class K>
	class set
	{
		struct KeyOfT
		{
			const K& operator()(const K& k)
			{
				return k;
			}
		};

	public:

		typedef typename RBTreeNode<K> Node;
		typedef typename RBTree<K, K, KeyOfT>::const_iterator iterator;
		typedef typename RBTree<K, K, KeyOfT>::const_iterator const_iterator;
		typedef typename RBTree<K, K, KeyOfT>::const_reverse_iterator reverse_iterator;
		typedef typename RBTree<K, K, KeyOfT>::const_reverse_iterator const_reverse_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();
		}

		reverse_iterator rbegin()
		{
			return _t.rbegin();
		}

		reverse_iterator rend()
		{
			return _t.rend();
		}

		const_reverse_iterator rbegin()const
		{
			return _t.rbegin();
		}

		const_reverse_iterator rend()const
		{
			return _t.rend();
		}

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

		iterator Find(const K& key)
		{
			Node* cur = _t.find(key);
			return iterator(cur);
		}

	private:
		RBTree<K, K, KeyOfT> _t;
	};
}

map.h

#pragma once
#include"rb_tree.h"


namespace MapSimulate
{
	template<class K, class V>
	class map
	{
		struct MapOfKey
		{
			const K& operator()(const pair<const K, V>& kv)
			{
				return kv.first;
			}
		};

	public:
		typedef typename RBTreeNode<pair<const K, V>> Node;
		typedef typename RBTree<K, pair<const K,V>, MapOfKey>::iterator iterator;
		typedef typename RBTree<K, pair<const K,V>, MapOfKey>::const_iterator const_iterator;
		typedef typename RBTree<K, pair<const K, V>, MapOfKey>::reverse_iterator reverse_iterator;
		typedef typename RBTree<K, pair<const K, V>, MapOfKey>::const_reverse_iterator const_reverse_iterator;

		pair<iterator,bool> insert(const pair<const 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;
		}

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

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

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

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


		reverse_iterator rbegin()
		{
			return _t.rbegin();
		}

		reverse_iterator rend()
		{
			return _t.rend();
		}

		const_reverse_iterator rbegin()const
		{
			return _t.rbegin();
		}

		const_reverse_iterator rend()const
		{
			return _t.rend();
		}

		iterator Find(const K& key)
		{
			Node* cur = _t.find(key);
			return iterator(cur);
		}

	private:
		RBTree<K, pair<const K, V>, MapOfKey> _t;
	};
}

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

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

相关文章

2023.05.28 学习周报

文章目录 摘要文献阅读1.题目2.现有方法存在的局限性3.SR-GNN模型4.模型的组成部分4.1 构图4.2 item向量表示4.3 session向量表示4.4 预测模块 5.实验与分析5.1 数据集5.2 比较方法5.3 评估指标5.4 实验结果 6.结论 有限元法1.一个例子2.进一步 深度学习1.张量场2.对流-扩散方程…

Linux(基础IO详解)

在基础IO这篇博客中&#xff0c;我们将了解到文件系统的构成&#xff0c;以及缓冲区究竟是个什么东东&#xff0c;我们都知道缓冲区&#xff0c;有时也谈论缓冲区&#xff0c;但不一定真的去深入了解过缓冲区。为什么内存和磁盘交互速度如此之慢&#xff1f;为什么都说Linux中一…

Dom解析与Sax解析的区别

1.Dom解析&#xff1a; Dom解析的时候&#xff0c;首先要把整个文件读取完毕&#xff0c;装载到内存中。然后进行解析&#xff0c;在解析的过程中&#xff0c;你可以直接获取某个节点&#xff0c;进行操作&#xff0c;也可以获取根节点然后进行遍历操作&#xff0c;得到所有的…

一台服务器通过apache安装多个web应用

当我们只有一台linux服务器资源但有创建多个网站的需求时&#xff0c;我们可以通过安装一个网站服务器Apache进行搭建&#xff0c;此次服务器使用Centos 7 下面分别介绍一个域名多个端口和多个域名用Apache来搭建多个网站的操作过程。 一、使用apache 服务器 &#xff08;一…

HCIA-MSTP替代技术之链路捆绑(LACP模式)

目录 手工链路聚合的不足&#xff1a; LACP链路聚合的原理 LACP模式&#xff1a; LACPDU&#xff1a; 1&#xff0c;设备优先级&#xff1a; 设备优先级的比较是&#xff1a;先比较优先级大小&#xff0c;0到32768&#xff0c;越小优先级越高&#xff0c;如果优先级相同&a…

华为FinalMLP

FinalMLP:An Enhanced Two-Stream MLP model for CTR Prediction 摘要 Two-Stream model&#xff1a;因为一个普通的MLP网络不足以学到丰富的特征交叉信息&#xff0c;因此大家提出了很多实用MLP和其他专用网络结合来学习。 MLP是隐式地学习特征交叉&#xff0c;当前很多工作…

分布式网络通信框架(二)——RPC通信原理和技术选型

项目实现功能 技术选型 黄色部分&#xff1a;设计rpc方法参数的打包和解析&#xff0c;也就是数据的序列化和反序列化&#xff0c;用protobuf做RPC方法调用的序列化和反序列化。 使用protobuf的好处: protobuf是二进制存储&#xff0c;xml和json是文本存储&#xff1b; pro…

哈希应用: 位图 + 布隆过滤器

文章目录 哈希应用: 位图 布隆过滤器1. 位图1.1 提出问题1.2 位图概念1.3 位图实现1.4 位图应用1.4.1 变形题1代码 1.4.2 变形题21.4.3 找文件交集思路1思路2 1.4.4 总结 1.5 位图优缺点 2. 哈希切割3. 布隆过滤器3.1 提出问题3.2 布隆过滤器概念3.3 布隆过滤器的各个接口3.3.…

MySQL---优化(insert、order by 、group by 、limit、子查询)

1. insert语句优化 当进行数据的insert操作的时候&#xff0c;可以考虑采用以下几种优化方案: -- 如果需要同时对一张表插入很多行数据时&#xff0c;应该尽量使用多个值表的insert语句&#xff0c;这种方式将大大的缩减 -- 客户端与数据库之间的连接、关闭等消耗。使得效率比…

R-Meta分析与【文献计量分析、贝叶斯、机器学习等】多技术融合实践与拓展

Meta分析是针对某一科研问题&#xff0c;根据明确的搜索策略、选择筛选文献标准、采用严格的评价方法&#xff0c;对来源不同的研究成果进行收集、合并及定量统计分析的方法&#xff0c;最早出现于“循证医学”&#xff0c;现已广泛应用于农林生态&#xff0c;资源环境等方面。…

ARM体系结构

目录 ARM体系架构 一、ARM公司概述 二、ARM产品系列 三、指令、指令集 指令 指令集 ARM指令集 ARM指令集 Thumb指令集 &#xff08;属于ARM指令集&#xff09; 四、编译原理 五、ARM数据类型 字节序 大端对齐 小端对齐 六、ARM工作模式 1.AR…

Java中synchronized锁的深入理解

使用范围 synchronized使用上用于同步方法或者同步代码块在锁实现上是基于对象去实现使用中用于对static修饰的便是class类锁使用中用于对非static修饰的便是当前对象锁 synchronized的优化 在jdk1.6中对synchronized做了相关的优化 锁消除 在synchronized修饰的代码块中…

如何实现局域网下设备之间的互通互联和外网访问?

两台电脑怎么在同一路由下访问共享文件夹&#xff1f;两台不同系统的电脑在同一个路由器下访问共享文件夹进行数据共享&#xff0c;从本质上说就是在同一个局域网下设备之间的互通互联&#xff0c;这就需要我们搭建一个内网文件共享服务器来实现此功能 &#xff0c;比如常见的W…

linux系统中通配符与常用转义字符

通配符 在平时我们使用使用linux系统的过程中会遇到忘记文件名称的问题&#xff0c;这时候呢&#xff0c;通配符就发挥它的作用啦。 顾名思义啦&#xff0c;通配符就是用来匹配信息的符号&#xff0c;如何&#xff08;*&#xff09;代表零个或多个字符&#xff0c;&#xff08;…

Unity烟花特效实现(附源码)

Unity烟花特效 附代码 写在前面效果代码地址核心步骤 写在后面 写在前面 朋友过生&#xff0c;不知道送什么礼物&#xff0c;就想着用自己所学知识做个特效当礼物吧&#xff0c;嘿。 主要参考了 这位up的视频 &#xff0c;感谢 效果 代码地址 https://github.com/hahahappyb…

【LeetCode热题100】打开第5天:最长回文子串

文章目录 最长回文子串⛅前言&#x1f512;题目&#x1f511;题解 最长回文子串 ⛅前言 大家好&#xff0c;我是知识汲取者&#xff0c;欢迎来到我的LeetCode热题100刷题专栏&#xff01; 精选 100 道力扣&#xff08;LeetCode&#xff09;上最热门的题目&#xff0c;适合初识…

linux高级---k8s中的五种控制器

文章目录 一、k8s的控制器类型二、pod与控制器之间的关系三、状态与无状态化对特点四、Deployment1、Deployment的资源清单文件2、在配置清单中调用deployment控制器3、镜像更新4、金丝雀发布5、删除Deployment 五、Statefulset六、DaemonSet1、daemonset的资源清单文件2、在配…

车载T-BOX

Telematics BOX&#xff0c;简称车载T-BOX&#xff0c;车载T-BOX主要用于和后台系统/手机APP通信&#xff0c;实现手机APP的车辆信息显示与控制 目录 1、车载T-BOX的定义 2、车载T-BOX的主要功能 2.1、数据采集和存储 2.2、远程查询和控制 2.3、道路救援 2.4、故障诊断 …

vue2_计算属性

目录 计算属性 计算属性缓存vs方法 计算属性vs侦听属性 getter和setter 计算属性和监听器 前端调用api实现问答 侦听器 计算属性 鉴于能在插值表达式中写js表达式&#xff1b;这样做也一定程度上违背了设计插值表达式的初衷&#xff1b;特别是&#xff1a; 其实就相当于…

nginx(七十九)rewrite模块指令再探

一 rewrite模块再探 ① 知识回顾 1) 结合自己遇到过的案例场景2) 关注一些易错点、难点3) 本文内容很杂,建议读者选取感兴趣的阅读 rewrite模块 rewrite功能 ② nginx中利用if 等价&&多条件 需求背景&#xff1a; 1) nginx不支持&&、||、and、or等逻辑…