目录

1.如何通过红黑树封装出set和map

2.set和map迭代器的实现

3.如何确保map和set中K不能被修改的问题

4.map operator[]的实现

5.代码汇总


我们都知道set和map的底层都是通过红黑树来实现的,前面讲解了红黑树的原理和代码,那么如何通过红黑树的代码封装实现set和map呢。

1.如何通过红黑树封装出set和map

set模型是key模型,只存关键字key,而map是key-value模型,存的的pair<K,V>,那如何通过模板参数来控制红黑树是哪种模型,实例化不同的模板。我们学习一下库里面是如何实现的。

这里面通过了多层的封装来实现了如果是set,红黑树的节点就存Key,如果是map,红黑树节点就存pair<K,V>。符合了set和map的模型。

红黑树的第一个模板参数就是关键字K的类型,第二个模板参数就是所要存储数据的类型,set底层就存储K,而map底层就存储pair<K,V>。

但是这里面还有一个问题,向set和map中插入节点时,set插入的是key,可以根据二叉搜索树的规则进行比较,而map插入的是pair<K,V>,是无法进行比较的,这个问题是如何来解决的呢?

这个就需要看红黑树的第三个模板参数了。

set中实现了一个内部类:

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

map中也实现了一个内部类:

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

这两个类都实现了operator(),其实就是一个仿函数,返回了关键字K的值,这样无论是map和set都可以根据二叉搜索树的规则进行比较找到要插入的位置了。

2.set和map迭代器的实现

下面的代码是迭代器实现的关键代码,其中最重要的就是operator++和operator--。通过三个模板参数可以控制该迭代器是const迭代器还是普通迭代器,其实就是通过传不同的参数类型来实现的,注意迭代器是像指针那样使用,用来方便遍历,但并不是指针,他的实质就是operator*,operator++,operator!=等函数重载实现的,函数重载的返回值是我们想要的结果。

//用来适配普通迭代器和const迭代器
template<class T,class Ptr,class Ref>
struct __RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __RBTreeIterator<T,Ptr,Ref> self;
	
	__RBTreeIterator(Node* node)
		:_node(node)
	{}

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

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

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

	bool operator !=(const self& s) const
	{
		return _node != s._node;
	}
	//这里走的是中序遍历 左 根 右
	self& operator++()
	{
		//1.右不为空
		if (_node->_right)
		{
			//右子树的最左节点
			Node* rightMin = _node->_right;
			while (rightMin->_left)
			{
				rightMin = rightMin->_left;
			}
			_node = rightMin;
		}
		//2.右为空 说明该子树已经遍历完了 向上找孩子是父亲左的那个祖宗节点
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;

			while (parent && parent->_right == cur)
			{
				parent = parent->_parent;
				cur = cur->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	//注意 这里是 右 根 左
	self& operator--()
	{
		//1.左不为空
		if (_node->_left)
		{
			//左子树的最右节点
			Node* leftMax = _node->_right;
			while (leftMax->_right)
			{
				leftMax = leftMax->_right;
			}
			_node = leftMax;
		}
		//2.左为空 说明这颗子树也已经遍历完了 找孩子是父亲右的那个祖宗节点
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && parent->_left == cur)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	Node* _node;
};

operator++:

operator--:

3.如何确保map和set中K不能被修改的问题

set是如何保证key是不能被修改的呢?

我们看到set的普通迭代器就是const迭代器,但是我们不能只写const迭代器,因为我们用的时候还是set<K>::iterator it=s.begin(),s是一个set容器等之类的用法。我们这样写只是为了保证set中K的值不被修改。

map是如何保证key是不能被修改的呢?

我们能不能借用set的思路呢,显然是不可以的,如果和set一样,那么map中不仅key不能被修改,value也不能被修改,那不就不符合要求了。

map是这样实现的:

map中存的红黑树对应的红黑树节点所存的pair中的K是不允许被修改的是const的,对iterator解引用返回的是pair<const K,V>确保了Key不被修改。

4.map operator[]的实现

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;
}

主要是修改insert函数的返回,通过调用insert函数来实现operator[]。

5.代码汇总

MyMap.h

#pragma once
#include"RBTree.h"

namespace my
{
	template<class K,class V>
	class map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<const 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();
		}

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

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

		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;
		}

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

MySet.h

#pragma once
#include"RBTree.h"

namespace my
{
	template<class K>
	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;

		//加const才能编译通过 
		// 因为这里的iterator其实就是const_iterator,如果不加const普通对象调用返回普通对象的迭代器是无法赋值给const对象的迭代器的
		//加const后,普通对象调用的也是const修饰的函数,返回的就是const迭代器 符合set key不能修改
		iterator begin() const 
		{
			return _t.begin();
		}

		iterator end() const
		{
			return _t.end();
		}
		//注意这里pair中的iterator实际为const_iterator 普通对象调用insert返回的是普通迭代器而不是const迭代器
		//所以不能直接把普通迭代器直接返回给const迭代器
		//我们需要在iterator中写一个构造函数,用普通迭代器构造const迭代器
		pair<iterator, bool> insert(const K& key)
		{
			pair<typename RBTree<K, K, SetKeyOfT>::iterator,bool>ret=_t.insert(key);
			return pair<iterator, bool>(ret.first, ret.second);

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

RBTree.h

#pragma once

#include<iostream>
using namespace std;

enum Col
{
	BLACK,
	RED
};

template<class T>
struct RBTreeNode
{

	RBTreeNode(const T& date)
		:_parent(nullptr)
		, _left(nullptr)
		, _right(nullptr)
		, _date(date)
		, _col(RED)
	{}


	RBTreeNode<T>* _parent;
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;

	T _date;
	Col _col;
};

//用来适配普通迭代器和const迭代器
template<class T,class Ptr,class Ref>
struct __RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __RBTreeIterator<T,Ptr,Ref> self;
	typedef __RBTreeIterator<T, T*, T&> Iterator; //注意Iterator和self是有区别的

	

	/*场景 1:类被实例化为const迭代器时
		此时 iterator 是 “普通迭代器类型”(非const),而当前类是const迭代器类。
		这个构造函数就会成为转换构造函数—— 允许用 “普通迭代器” 构造 “const迭代器”,
		从而支持const迭代器从普通迭代器那里获取节点信息(但const迭代器不会修改节点值)。
	场景 2:类被实例化为普通迭代器时
		此时 iterator 就是 “普通迭代器类型” 本身,
		这个构造函数就是普通的拷贝构造函数—— 用于同一类型迭代器之间的拷贝(比如普通迭代器的复制)。*/

	__RBTreeIterator(const Iterator& it) 
		:_node(it._node)
	{}


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

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

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

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

	bool operator !=(const self& s) const
	{
		return _node != s._node;
	}
	//这里走的是中序遍历 左 根 右
	self& operator++()
	{
		//1.右不为空
		if (_node->_right)
		{
			//右子树的最左节点
			Node* rightMin = _node->_right;
			while (rightMin->_left)
			{
				rightMin = rightMin->_left;
			}
			_node = rightMin;
		}
		//2.右为空 说明该子树已经遍历完了 向上找孩子是父亲左的那个祖宗节点
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;

			while (parent && parent->_right == cur)
			{
				parent = parent->_parent;
				cur = cur->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	//注意 这里是 右 根 左
	self& operator--()
	{
		//1.左不为空
		if (_node->_left)
		{
			//左子树的最右节点
			Node* leftMax = _node->_right;
			while (leftMax->_right)
			{
				leftMax = leftMax->_right;
			}
			_node = leftMax;
		}
		//2.左为空 说明这颗子树也已经遍历完了 找孩子是父亲右的那个祖宗节点
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && parent->_left == cur)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	Node* _node;

};

//这里的T模板参数代表的是红黑树节点要存在的数据类型 如果是set就存K,如果是map则存pair<K,V>
//K模板参数就是map和set中的键
template<class K, class T,class KeyOfT>
class RBTree
{
public:
	typedef RBTreeNode<T> Node;
	typedef __RBTreeIterator<T,T*,T&> iterator;
	typedef __RBTreeIterator<T, const T*,const T&> const_iterator;


public:
	iterator begin()
	{
		Node* leftMost = _root;
		while (leftMost && leftMost->_left)
		{
			leftMost = leftMost->_left;
		}
		return iterator(leftMost);
	}

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

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

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

	Node* find(const K& key)
	{
		Node* cur = _root;
		KeyOfT kot;

		while (cur)
		{
			if (kot(cur->_date) > key)
			{
				cur = cur->_left;
			}
			else if (kot(cur->_date) < key)
			{
				cur = cur->_right;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}

	pair<iterator,bool> insert(const T& date)
	{
		if (_root == nullptr)
		{
			_root = new Node(date);
			_root->_col = BLACK;
			return make_pair(iterator(_root),true);
		}

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

		while (cur)
		{
			if (kot(cur->_date) > kot(date))
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (kot(cur->_date) < kot(date))
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return make_pair(iterator(cur), true);
			}
		}

		//找到要插入的位置 进行链接
		cur = new Node(date);
		Node* newNode = cur;
		if (kot(parent->_date) > kot(date))
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
		cur->_parent = parent;

		//进行调整 父亲节点存在且为红 才需要调整 默认插入的节点是红色的
		while (parent && parent->_col == RED)
		{
			Node* gradeparent = parent->_parent; //gradeparent节点一定存在 否则不会进入这里 直接就插入成功了
			if (parent == gradeparent->_left)
			{
				Node* uncle = gradeparent->_right;
				//叔叔节点存在且为红
				if (uncle && uncle->_col == RED)
				{
					//变色+继续向上调整
					uncle->_col = parent->_col = BLACK;
					gradeparent->_col = RED;

					//向上调整 此时parent相当于cur
					cur = gradeparent;
					parent = cur->_parent;
				}
				//叔叔节点不存在 or 叔叔节点存在为黑
				else
				{
					//旋转加变色
					if (cur == parent->_left)
					{
						//    g
						//  p
						//c
						RotateR(gradeparent);
						parent->_col = BLACK;
						gradeparent->_col = RED;
					}
					else
					{
						//    g
						//  p
						//    c

						RotateL(parent);
						RotateR(gradeparent);
						cur->_col = BLACK;
						gradeparent->_col = RED;
					}

					break;
				}

			}
			else
			{
				Node* uncle = gradeparent->_left;

				//叔叔存在且为红
				if (uncle && uncle->_col == RED)
				{
					//变色+向上调整
					uncle->_col = parent->_col = BLACK;
					gradeparent->_col = RED;

					cur = gradeparent;
					parent = cur->_parent;
				}
				//叔叔不存在 or 叔叔存在且为黑
				else
				{
					//旋转+变色
					if (cur == parent->_right)
					{
						//g
						//  p
						//    c

						RotateL(gradeparent);

						parent->_col = BLACK;
						gradeparent->_col = RED;
					}
					else
					{
						//g
						//  p
						//c

						RotateR(parent);
						RotateL(gradeparent);

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

					break;
				}

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

	void RotateR(Node* parent)
	{
		Node* cur = parent->_left;
		Node* curRight = cur->_right;
		Node* ppNode = parent->_parent;

		//进行链接
		parent->_left = curRight;
		if (curRight)
			curRight->_parent = parent;

		cur->_right = parent;
		parent->_parent = cur;

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

	void RotateL(Node* parent)
	{
		Node* cur = parent->_right;
		Node* curLeft = cur->_left;
		Node* ppNode = parent->_parent;
		parent->_right = curLeft;
		if (curLeft)
		{
			curLeft->_parent = parent;
		}
		cur->_left = parent;
		parent->_parent = cur;
		if (ppNode == nullptr)
		{
			_root = cur;
			cur->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = cur;
			}
			else
			{
				ppNode->_right = cur;
			}
			cur->_parent = ppNode;
		}
	}

private:
	Node* _root = nullptr;
};

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐