模拟map/set的实现

map和set的底层都是红黑树,而我们在前面已经了解了红黑树的实现,所以我们现在来模拟实现map和set。

一.STL库中map和set的实现

set是key的搜索环境,而map是key/value的搜索场景,那么库里面是否实现了两棵红黑树来分别支持set和map呢?

答案是:NO!

可是set和map明明是两种不同的环境,一棵树的节点是怎么解决既存储一个ke又存储key/value的?

这就在于库中实现红黑树时多增加了一个模板参数

它们都使用了tree.h中的rb_tree,而rb_tree的节点是一个派生类,它存储的是一个value类型,而我们追根溯源到map和set中,它们分别是pair<key,value>、key。

当我们在set和map里面使用re_tree时,多传了一个参数:set多传了一个key,map多传了一个pair<key,value>。然后到底层的rb_tree时,第二个模板参数接收,然后红黑树节点用这第二个模板参数来作为节点存储的数据的类型。

那么我们是不是只需要传第二个模板参数就可以了,第一个没必要传了?况且对于set来说,两个参数是相同的。

答案当然是不行的。因为set和map还有find和erase接口,它们都是根据key来操作的,如果只传第二个模板参数,那么map的pair获取k时,就会导致函数接口不一致了,所以第一个模板参数k,是用来find和erase场景使用的。

二.模拟实现set与map

1.set与map的结构

因为我们先前已经实现了红黑树,这里我们只需要对红黑树进行一些修改即可。

我们这里也给红黑树多增加一个模板参数,用此模板参数来作为红黑树节点存储的数据的类型。所以set和map在使用红黑树作为底层传参时需要多传一个模板参数:set多传一个K,map多传一个pair<K,V>。

其实对于set来说,第二个参数确实有点多余,但是为了契合map,所以就不得不委屈一下。

Mymap.h
namespace xsc
{
	template<typename K,typename V>
	class map
	{
	private:
		RBTree<K, pair<K, V>> tree;
	};
}

Myset.h
namespace xsc
{
	template<typename K>
	class set
	{
	private:
		RBTree<K,K> tree;
	};
}

RBTree.h
//红黑树节点
template <typename Value>
struct RBTreeNode
{
	Value _date;
	RBTreeNode<Value>* _left;
	RBTreeNode<Value>* _right;
	RBTreeNode<Value>* _parent;
	COLOUR colour;

	RBTreeNode(const Value& data)
		: _data(data)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
	{}
};

//红黑树
template <typename K, typename Value>
class RBTree
{
	typedef RBTreeNode<Value> Node;
private:
	Node* _root = nullptr;
};


2.insert

我们在之前的红黑树中已经实现了insert,只不过我们改变了红黑树的一些结构,所以插入也要跟着修改。因为之前红黑树只是key/value的搜索场景,而我们在实现set和map时使用了同一棵红黑树,既要实现key也要实现key/value,因此引入了第二个模板参数,所以导致了insert的改变

因为value既可以是k,也可能是pair<K,V>。所以我们不能像以往那样直接插入一个pair,而是要插入一个value。 

还有一个要注意的就是,我们插入的时候按照的是二叉搜索树的插入逻辑,大的往右走,小的往左走,这个比较的逻辑也要修改。因为直接插入的是pair,所以直接比较first;但是现在插入的value既可能是k,有可能是pair<k,v>,对于后者了来说,可以这样比较,但是对于k来说,做不到!!!

为了解决这个问题,我们可以给红黑树再引入一个模板参数, 这个模板参数是一个仿函数。

虽然我们在底层的红黑树不知道是哪种搜索场景,但是我们在上层set和map知道我们节点里面存储的是什么,所以我们可以在这两个类里面分别写一个仿函数,然后我们将这个仿函数传给红黑树,红黑树用这个仿函数来获取节点date中我们想要用来比较的数据

//列举出一部分需要比较的场景
KeyOfKey kok;
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
	if (kok(cur->_data) < kok(data))
	{
		parent = cur;
		cur = cur->_right;
	}
	else if (kok(cur->_data) > kok(data))
	{
		parent = cur;
		cur = cur->_left;
	}
	else
	{
		return false;
	}
}

3.iterator

我们之前在AVL树和红黑树中并没有涉及迭代器,而是直接使用递归的方式中序遍历了二叉树。而我们现在实现迭代器要一个一个结点的走,而不是一个函数调用直接遍历完了。

这里实现迭代器的方式与list的迭代器类似,我们用了一个类来封装结点的指针,再通过运算符重载,以此来作为迭代器。

template<typename T,typename Ref, typename Ptr>
struct RBTree_Iterator
{
	typedef RBTreeNode<T> Node;
	typedef RBTree_Iterator<T, Ref, Ptr> Self;

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

	Node* _node;
};

3.1begin()

begin返回开始的迭代器,而红黑树只有中序遍历才有意义,所以我们要按照中序遍历二叉树,而中序的第一个,就是二叉树的最左节点(最小节点)。

Iterator Begin()
{
	Node* cur = _root;
	while (cur && cur->_left)
	{
		cur = cur->_left;
	}

	return Iterator(cur);
}

这里返回的是一个迭代器对象,所以我们要用结点的指针构造出来一个迭代器对象。  因为有可能是空树,所以我们也要加一个判断条件。

3.2operator++

operator++要实现让迭代器走到下一个结点的位置,因为我们是按照中序的顺序遍历二叉树,所以++后的下一个位置就是中序的下一个位置。而中序是按照左子树-根-右子树来遍历的。

我们++时,先判断当前结点的右子树是否为空,如果不为空,就让迭代器走到右子树的最左节点

如果右子树为空,说明这棵子树已经访问完了,要走到中序的下一个位置 。我们用cur来记录当前it的位置,parent = cur->parent;如果cur是parent的左孩子,那么it就走到parent的位置,如果不是,那就继续往上走,cur = parent;parent = cur->parent;

为什么可以这样呢?右子树为空,说明说明这棵子树已经完了,而中序的顺序是左子树-根-右子树,当cur是parent的右孩子时,说明以parent为根的这颗子树也完了,要继续往上走,直到cur是parent的左孩子,这就说明以parent为根的这棵子树左子树完了,接下来就该访问根了,所以这个时候我们就让it走到parent的位置。

当我们it走到根时,且右子树不为空,那么下一个位置就是右子树的最左节点,下面的逻辑与上面的一致,右子树为空,就往上走,找到cur是parent的左孩子的根,遍历根,如果右子树不为空,就走到右子树的最左节点。

当我们走到中序的最后一个时,开始向上返回,但是一直不会遇到cur是parent的左孩子这种情况,所以cur最后会走到根,parent会走到空。此时就应该结束遍历了,所以我们可以让end()等于nullptr,当parent == nullptr时,it就走到parent的位置,然后while的判断条件就为假,退出循环。

Self& operator++()
{
	//右子树不为空,走到右子树的最左节点
	if (_node->_right)
	{
		Node* MostLeft = _node->_right;
		while (MostLeft->_left)
		{
			MostLeft = MostLeft->_left;
		}
		_node = MostLeft;
	}
	else
	{
		//右子树为空,往上走,找到cur是parent的左孩子的那个parent
		Node* cur = _node;
		Node* parent = cur->_parent;
		while (parent && cur == parent->_right)
		{
			cur = parent;
			parent = cur->_parent;
		}
		_node = parent;
	}
	return *this;
}

3.3end() 

为了保证能够正常退出循环,而parent最后会走到空,所以我们让end也等于空,最后it == end ==nullptr,就不会进循环。

Iterator End()
{
	return Iterator(nullptr);
}

3.4其他成员函数

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

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

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

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

这里的operator->是为了针对map,因为map直接解引用的话是一个pair,还得再访问pair里面的k和v,为了简便,我们可以yongoperator->直接访问pair里面的first和second。

3.5声明红黑树迭代器

我们这里以set为例,map与之类似:

我们在set里面声明迭代器时,要声明类域,但是我们借助域作用限定符声明时,编译器并不知道我们取得这个是一个类型还是一个静态成员变化。如果是类型的话,这样写就没毛病,如果是静态成员的话,这样就不合乎语法。所以我们在前面加一个typename,意思就是告诉编译器,这是个类型,你不要报错。

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

3.6测试迭代器

测试结果:

//测试代码
int main()
{
	xsc::set<int> si;
	si.insert(4);
	si.insert(2);
	si.insert(5);
	si.insert(1);
	si.insert(6);

	auto it = si.begin();
	while (it != si.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

	for (auto e : si)
	{
		cout << e << " ";
	}
	cout << endl;

	xsc::map<string, string> dict;
	dict.insert({ "sort", "排序" });
	dict.insert({ "left", "左边" });
	dict.insert({ "right", "右边" });

	auto di = dict.begin();
	while (di != dict.end())
	{
		cout << di->first << ":" << di->second << endl;
		++di;
	}
	cout << endl;

	return 0;
}

3.7operator--

我们这里并没有涉及反向迭代器,但是我们通过从end开始,借助operator--来实现反向遍历:

auto it = si.end();
while (it != si.begin())
{
    --it;
	cout << *it << " ";
}

我们end是根节点的父亲——nullptr,所以当我们要从end开始--,我们要首先判断,如果_node == nullptr,那就找到二叉树得最右节点(最大节点),让it指向这个位置。

而在这种情况,我们要找到二叉树的最右节点就需要根节点,而我们在迭代器里面并没有根结点的指针。所以,我们在封装迭代器时还要加上根节点的指针。

template<typename T,typename Ref, typename Ptr>
struct RBTree_Iterator
{
	typedef RBTreeNode<T> Node;
	typedef RBTree_Iterator<T, Ref, Ptr> Self;

	RBTree_Iterator(Node* node,Node* root)
		:_node(node)
        ,_root(root)
	{}

	Node* _node;
    Node* _root;
};

 正常的中序是左子树-根-右子树,而我们反着走时顺序为右子树-根-左子树。所以operator--得实现逻辑和operator++是什么类似的。

当一个结点的左子树为空时,说明以这个节点为根的子树已经遍历完了,我们需要找到反向中序得下一个,我们让cur指向it,parent指向cur->parent;当cur是parent的右孩子时,说明此时就该访问这个parent了。

 当一个结点的左子树不为空时,那就访问左子树的最右节点(最大节点)。

Self& operator--()
{
	if (_node == nullptr)
	{
		Node* MostRight = _root;
		while (MostRight && MostRight->_right)
		{
			MostRight = MostRight->_right;
		}
		_node = MostRight;
	}
	else if (_node->_left)
	{
		//左子树不为空,访问左子树的最右节点/最大节点
		Node* MostRight = _node->_left;
		while (MostRight->_right)
		{
			MostRight = MostRight->_right;
		}
		_node = MostRight;
	}
	else
	{
		//左子树为空,往上遍历,找到cur == parent->_right
		Node* cur = _node;
		Node* parent = cur->_parent;
		while (parent && cur == parent->_left)
		{
			cur = parent;
			parent = cur->_parent;
		}
		_node = parent;
	}

	return *this;
}

 需要注意的是,在左子树为空的情况时,里面的while循环必须加上parent为空的情况,因为如果it位于整棵树的根,且无左子树时,此时cur指向root,parent指向空,如果不加上这个条件,在循环内部就会出现对空指针的解引用。 

4.stl库中红黑树的迭代器实现

在库中,红黑树有一个额外的哨兵位的头节点,它和根节点互为对方父亲,且它的左孩子为最左节点,右孩子为最右节点。

 我们实现的迭代器end指向根节点的父亲——nullptr,而库中end指向哨兵位header。当库中采用迭代器进行中序遍历时,遍历到最右节点就开始往上返,最后parent就会走到哨兵位的头节点,也就是end,此时就遍历结束。

这种方式的便利的一点就是找最左和最右节点时很方便,直接找header的左右孩子即可。 

5.const_iterator

RBTree.h
typedef RBTree_Iterator<Value, Value&, Value*> Iterator;
typedef RBTree_Iterator<Value, const Value&, Value*> Const_Iterator;

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

	return Iterator(cur, _root);
}

Const_Iterator End() const
{
	return Iterator(nullptr, _root);
}

Myset.h
typedef typename RBTree<K, K, KeyOfT>::Const_Iterator const_iterator;
const_iterator begin() const
{
	return tree.Begin();
}

const_iterator end() const
{
	return tree.End();
}

6.set和map的迭代器不支持修改 

set是key的搜索场景,map是key/value的搜索场景,而set中key是不允许被修改的,即使是普通迭代器,而对于map来说,即使是普通迭代器,它的key也不支持修改,value可以修改。

但是我们现在实现的迭代器不论是set的key还是map的key/value,都是可以修改的

那么我们要如何实现库中的效果呢?

我们迭代器使用的其实是在上层set/map传给红黑树的第二个模板参数,我们可以直接传const过去,这样无论是普通迭代器还是const迭代器都用的是const对象,自然不可以修改。只不过对于map来说,加const时,要加到pair里面的first。

加上之后,对于set来说,普通迭代器不能修改,对于map来说,普通迭代器key不能修改,value可以修改

 7.map支持operator[]

在学习map的时候时我们已经知道map的operator[]其实就是调用了insert来实现的。insert的返回值是一个pair<iterator,bool>。

operator既有插入的功能,也有查找+修改的功能。

V& operator[](const K& key)
{
	pair<iterator, bool> ret = tree.Insert({ key, V() });

	return ret.first->second;
}

三.map/set/RBTree代码

//Mymap.h
#include "RBTree.h"

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

		typedef typename RBTree<K, pair<const K, V>, KeyOfValue>::Iterator iterator;
		typedef typename RBTree<K, pair<const K, V>, KeyOfValue>::Const_Iterator const_iterator;

	public:
		iterator begin()
		{
			return tree.Begin();
		}

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

		const_iterator begin() const
		{
			return tree.Begin();
		}

		const_iterator end() const
		{
			return tree.End();
		}

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

		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = tree.Insert({ key, V() });

			return ret.first->second;
		}

	private:
		RBTree<K, pair<const K, V>, KeyOfValue> tree;
	};
}
//Myset.h

#include "RBTree.h"

namespace xsc
{
	template<typename K>
	class set
	{
		struct KeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

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

	public:
		iterator begin()
		{
			return tree.Begin();
		}

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

		const_iterator begin() const
		{
			return tree.Begin();
		}

		const_iterator end() const
		{
			return tree.End();
		}

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

	private:
		RBTree<K,const K, KeyOfT> tree;
	};
}
//RBTree.h
#pragma once

#include <iostream>
#include <cassert>

using namespace std;

enum COLOUR
{
	RED,
	BLACK
};

template <typename Value>
struct RBTreeNode
{
	Value _data;
	RBTreeNode<Value>* _left;
	RBTreeNode<Value>* _right;
	RBTreeNode<Value>* _parent;
	COLOUR colour;

	RBTreeNode(const Value& data)
		: _data(data)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
	{}
};

template<typename T,typename Ref, typename Ptr>
struct RBTree_Iterator
{
	typedef RBTreeNode<T> Node;
	typedef RBTree_Iterator<T, Ref, Ptr> Self;

	RBTree_Iterator(Node* node, Node* root)
		:_node(node)
		,_root(root)
	{}

	Self& operator++()
	{
		//右子树不为空,走到右子树的最左节点
		if (_node->_right)
		{
			Node* MostLeft = _node->_right;
			while (MostLeft->_left)
			{
				MostLeft = MostLeft->_left;
			}
			_node = MostLeft;
		}
		else
		{
			//右子树为空,往上走,找到cur是parent的左孩子的那个parent
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = cur->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	Self& operator--()
	{
		if (_node == nullptr)
		{
			Node* MostRight = _root;
			while (MostRight && MostRight->_right)
			{
				MostRight = MostRight->_right;
			}
			_node = MostRight;
		}
		else if (_node->_left)
		{
			//左子树不为空,访问左子树的最右节点/最大节点
			Node* MostRight = _node->_left;
			while (MostRight->_right)
			{
				MostRight = MostRight->_right;
			}
			_node = MostRight;
		}
		else
		{
			//左子树为空,往上遍历,找到cur == parent->_right
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = cur->_parent;
			}
			_node = parent;
		}

		return *this;
	}

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

	Ptr operator->()
	{
		return &(_node->_data);
	}
	
	bool operator==(const Self& s) const
	{
		return _node == s._node;
	}

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

	Node* _node;
	Node* _root;
};

template <typename K, typename Value, typename KeyOfKey>
class RBTree
{
	typedef RBTreeNode<Value> Node;
	
public:
	typedef RBTree_Iterator<Value, Value&, Value*> Iterator;
	typedef RBTree_Iterator<Value, const Value&, Value*> Const_Iterator;

	Iterator Begin()
	{
		Node* cur = _root;
		while (cur && cur->_left)
		{
			cur = cur->_left;
		}

		return Iterator(cur,_root);
	}

	Iterator End()
	{
		return Iterator(nullptr,_root);
	}

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

		return Iterator(cur, _root);
	}

	Const_Iterator End() const
	{
		return Iterator(nullptr, _root);
	}


	pair<Iterator,bool> Insert(const Value& data)
	{
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->colour = BLACK;

			return { Iterator(_root,_root),true };
		}
		KeyOfKey kok;
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (kok(cur->_data) < kok(data))
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kok(cur->_data) > kok(data))
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return { Iterator(cur,_root),false };
			}
		}

		cur = new Node(data);
		cur->colour = RED;
		if (kok(parent->_data) < kok(data))
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		// 链接父亲
		cur->_parent = parent;
		Node* newnode = cur;

		//当插入之后cur的父亲为红,则需要调整
		while (parent && parent->colour == RED)
		{
			Node* grandfather = parent->_parent;

			if (grandfather->_left == parent)
			{
				//     g
				//  p      u

				Node* uncle = grandfather->_right;

				//uncle存在且为红色
				if (uncle && uncle->colour == RED)
				{
					parent->colour = BLACK;
					uncle->colour = BLACK;
					grandfather->colour = RED;

					//更新cur,parent,继续向上调整
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					//uncle不存在 或者 uncle存在且为黑

					if (cur == parent->_left)
					{
						//      g
						//   p      u
						//c

						RotateR(grandfather);
						parent->colour = BLACK;
						grandfather->colour = RED;
					}
					else
					{
						//      g
						//   p      u
						//     c

						RotateL(parent);
						RotateR(grandfather);
						cur->colour = BLACK;
						grandfather->colour = RED;
					}
					break;
				}
			}
			else
			{
				//     g
				//  u      p

				Node* uncle = grandfather->_left;

				//uncle存在且为红色
				if (uncle && uncle->colour == RED)
				{
					parent->colour = BLACK;
					uncle->colour = BLACK;
					grandfather->colour = RED;

					//更新cur,parent,继续向上调整
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					//uncle不存在 或者 uncle存在且为黑

					if (cur == parent->_right)
					{
						//     g
						//  u      p
						//            c

						RotateL(grandfather);
						parent->colour = BLACK;
						grandfather->colour = RED;
					}
					else
					{
						//     g
						//  u      p
						//      c

						RotateR(parent);
						RotateL(grandfather);
						cur->colour = BLACK;
						grandfather->colour = RED;
					}

					break;
				}
			}
		}

		//保证根永远是黑色
		_root->colour = BLACK;

		return { Iterator(newnode,_root),true };
	}

	Node* Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < key)
			{
				cur = cur->_right;
			}
			else if (cur->_kv.first > key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}

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

	int Size(){return _Size(_root);}

private:

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

	int _Size(Node* root){
		if (root == nullptr)
			return 0;
		return _Size(root->_left) + _Size(root->_right) + 1;
	}

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

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

		subL->_right = parent;
		parent->_parent = subL;

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

			subL->_parent = pParent;
		}
	}

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

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

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

			subR->_parent = pParent;
		}
	}

private:
	Node* _root = nullptr;
};

完~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值