STL — Map和Set的简易实现

Map和Set的简易实现





前面两篇博客我分别介绍了 Map容器的使用以及 Set容器的使用, 我们了解到它的用法以及明白了它的底部实现其实就是红黑树. 这个时候可能有人就不

了.底层是红黑树Map为什么可以存储一个主键一个键值. 而Set只拥有一个主键.这里就是STL的强大之处. 利用巧妙地方法极大的提升了代码的

复用. 当然 在实现之前我也看了Map和Set的源码. 我这里实现的只是最简易的Map和Set,并没有很多复杂的功能. 可以插入,删除,迭代器的加加.减

减操作. 好了 那我们进入正题.

首先思考第一个问题. Map和Set的元素个数不同.当然如果你说我给Map设计一个红黑树容器,再给Set设计一个红黑树容器.这样没问题,但是你的代码

不是但有点太长了.如果这里不是红黑树是一个很大的项目呢? 所以需要使用同一个红黑树的底层容器来提升代码复用.当然你可以让Map在这里传入

一个 pair<>,让Set在这里传入一 个Key,这个思想没问题.但红黑树里面需要用到键值之间的比较,Set在这里可以直接使用Key进行比较.但是Map的

pair 需要使用其中的.first来进 行比较? 那么这里应该如何实现呢? 这里需要用到仿函数的知识,以及巧妙的运用模板. 下面我贴出来代码

还有一张过程步骤图帮大家理解这个过程.

Map的封装代码:

#include"RBTree.h"

template
class MakeMap
{

public:
	typedef pair ValueType;

	struct KeyOfValue
	{
		const K& operator()(const ValueType& kv)
		{
			return kv.first;
		}
	};

	typedef typename RBTree::Iterator Iterator;

	pair Insert(const ValueType& v)
	{
		return _Tree.Insert(v);
	}

	V& operator[](const K& key)
	{
		pair ret = _Tree.Insert(make_pair(key, V()));
		//模板参数的V() 缺省值.
		return ret.first;
	}
	Iterator Begin()
	{
		return _Tree.Begin();
	}

	Iterator End()
	{
		return _Tree.End();
	}
private:
	RBTree _Tree;
};

void Test()
{
	MakeMap dict;

	dict.Insert(make_pair("liangliang", "亮亮"));
	dict.Insert(make_pair("MT", "梦婷"));
	dict.Insert(make_pair("Steam", "蓝洞"));
	dict.Insert(make_pair("type", "字节"));

	MakeMap::Iterator it = dict.Begin();

	while (it != dict.End())
	{
		cout << it->second << " ";
		++it;
	}
}

Set的封装代码:

#include"RBTree.h"

template
class mySet
{
public:
	typedef K ValueType;
	struct KeyOfKey
	{
		const ValueType& operator()(const ValueType& key)
		{
			return key;
		}
	};

	typedef typename RBTree::Iterator Iterator;
	//如果没有typename,编译器就会去RBTree里面去寻找Iterator.但是RBTree并没有实例化,所以会找不到
	//然后报错. 所以typename告诉编译器这个类型是一个模板的类型,现在先不要确定它的类型.

	pairinsert(const K& key)
	{
		return Tree.Insert(key);
	}

	Iterator Begin()
	{
		return Tree.Begin();
	}

	Iterator End()
	{
		return Tree.End();
	}


protected:

	RBTree Tree;
};


void Test()
{
	mySet T;

	T.insert(1);
	T.insert(2);
	T.insert(3);
	T.insert(4);
	T.insert(5);
	T.insert(6);
	T.insert(7);

	mySet::Iterator it = T.Begin();

	while (it != T.End())
	{
		cout << *it << " ";
		++it;
	}

	cout << endl;

}

RBTree的实现代码:

#include
#include
#include
#include
using namespace std;

enum colour
{
	RED,
	BLACK
};

template
struct RBTreeNode
{
	ValueType _valueField;
	RBTreeNode* _left;
	RBTreeNode* _right;
	RBTreeNode* _parent;

	colour _col;

	RBTreeNode(const ValueType& v)
		:_valueField(v)
		, _left(NULL)
		, _right(NULL)
		, _parent(NULL)
		, _col(RED)
	{}
};

template
struct __RBtreeIteartor
{
	typedef RBTreeNode Node;
	typedef __RBtreeIteartor self;

public:

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

	__RBtreeIteartor(const self& node)
		:_node(node._node)
	{}

	ValueType& operator*()
	{
		return _node->_valueField;
	}

	ValueType* operator->()
	{
		return &operator*();
	}

	self& operator=(const self& node)
	{
		_node = node._node;
	}

	self& operator++()
	{
		//1.如果右不为空,访问右树的最左节点
		//2.如果我的右为空,下一个访问的就是沿着这个路径往上找,第一个右树不是我的节点
		//然后访问该节点.
		if (_node->_right)
		{
			Node* subR = _node->_right;
			while (subR->_left)
			{
				subR = subR->_left;
			}
			_node = subR;
		}
		else
		{
			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->_left)
		{
			Node* subL = _node->_left;
			while (subL->_right)
			{
				subL = subL->_right;
			}
			_node = subleft;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = cur->_parent;
			}
			_node = parent;
		}
		return *this;
	}

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

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

private:
	Node* _node;

};

template
class RBTree
{
	typedef  V ValueType;
	typedef RBTreeNode Node;

public:

	typedef __RBtreeIteartor Iterator;

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

		return Iterator(cur);
	}

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

	pair Insert(const ValueType& v)
	{
		//_Insert(_root, x, y);
		if (_root == NULL)
		{
			_root = new Node(v);
			_root->_col = BLACK;
			return make_pair(Iterator(_root), true);
		}

		KeyOfValue keyofvalue;

		Node* cur = _root;
		Node* parent = cur;

		while (cur)
		{
			if (keyofvalue(cur->_valueField) > keyofvalue(v))
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (keyofvalue(cur->_valueField) < keyofvalue(v))
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (keyofvalue(cur->_valueField) == keyofvalue(v))
			{
				return make_pair(Iterator(cur), false);
			}
		}

		if (keyofvalue(parent->_valueField) > keyofvalue(v))
		{
			parent->_left = new Node(v);
			parent->_left->_parent = parent;
			cur = parent->_left;
		}
		else
		{
			parent->_right = new Node(v);
			parent->_right->_parent = parent;
			cur = parent->_right;
		}

		Node* newNode = cur;
		//目前父亲节点,插入节点,叔叔节点已经就绪.
		while (parent && parent->_col == RED)
		{
			Node* parentparent = parent->_parent;
			Node* uncle = NULL;

			if (parentparent->_left == parent)
				uncle = parentparent->_right;

			else
				uncle = parentparent->_left;

			if (uncle && uncle->_col == RED)
			{
				parentparent->_col = RED;
				parent->_col = BLACK;
				uncle->_col = BLACK;

				cur = parentparent;
				parent = cur->_parent;

			}
			else if (uncle == NULL || uncle->_col == BLACK)
			{
				if (parentparent->_left == parent)
				{
					if (parent->_left == cur)
					{
						RotateR(parentparent);
						parent->_col = BLACK;
					}
					else
					{
						RotateLR(parentparent);
						cur->_col = BLACK;
					}
				}
				else
				{
					if (parent->_right == cur)
					{
						RotateL(parentparent);
						parent->_col = BLACK;
					}
					else
					{
						RotateRL(parentparent);
						cur->_col = BLACK;
					}
				}
				parentparent->_col = RED;
				if (parentparent == _root)
				{
					_root = parent;
				}

			}
			else
			{
				assert(false);
			}
		}
		_root->_col = BLACK;
		return make_pair(Iterator(newNode), true);
		//担心经过旋转之后,找不到新增节点了,所以提前记录好.
	}


	Iterator Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (keyofvalue(cur->_valueField) > keyofvalue(key))
			{
				cur = cur->_right;
			}
			else if (keyofvalue(cur->_valueField) < keyofvalue(key))
			{
				cur = cur->_left;
			}
			else if (keyofvalue(cur->_valueField) == keyofvalue(key))
			{
				return Iterator(cur);
			}
		}
		return Iterator(NULL);
	}

protected:

	void RotateLR(Node*& parent)
	{

		RotateL(parent->_left);

		RotateR(parent);
	}

	void RotateRL(Node*& parent)
	{
		RotateR(parent->_right);
		RotateL(parent);
	}

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

		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;
		Node* ppNode = parent->_parent;
		subL->_right = parent;
		parent->_parent = subL;
		if (ppNode == NULL)
		{
			_root = subL;
			_root->_parent = NULL;
		}
		else
		{
			if (ppNode->_left == parent)
				ppNode->_left = subL;
			else
				ppNode->_right = subL;
			subL->_parent = ppNode;
		}
	}

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

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

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

		if (ppNode == NULL)
		{
			_root = subR;
			_root->_parent = NULL;
		}
		else
		{
			if (ppNode->_left == parent)
				ppNode->_left = subR;
			else
				ppNode->_right = subR;
			subR->_parent = ppNode;
		}
	}
private:
	Node* _root;
};

上面的代码我们可以看到. Set和Map其实就是一层马甲,对他们来说只是封装了底层的红黑树,只不过他们传入红黑树 KeyOfValue的参数不同.而后面

的KeyOfValue才是决定,RBTreeNode当中的_valueField与key比较时,返回的是Key还是pair<>.first. 我下面还有一幅图用来帮大家理解这整个复用

代码的框架. 大家仔细看一定会明白这里的KeyOfValue模板参数,以及_ValueType的作用. 如果理解这些那么Map和Set的简易实现应该就差不多了.

STL — Map和Set的简易实现_第1张图片

接下来我着重解释一下红黑树的迭代器,因为上一篇博客红黑树当中只是简单构建出来一颗红黑树,并没有对它的迭代器进行实现,那么现在我们继续

了解它的迭代器:

首先迭代器就是封装一层指针,让我们能够方便的遍历每一个容器.使用相同的方法. 也就是说我们不需要知道容器的底层实现. 就会遍历这个容器.

所以迭代器就是增加了封装性.给外边暴露一个接口,让你只会用就行了,不用知道为什么.红黑树的迭代器的operator*,operator->已经是老生常谈

了,所以我们今天的重点是明白迭代器的operator++()以为这是一个算法-> 因为红黑树遍历是中序遍历,所以我们只需要帮迭代器找到它下一个需要

访问的节点即可.  首先中序遍历的顺序是 左 中 右.  而我们这个算法就是找到中序遍历的下一个节点位置:

STL — Map和Set的简易实现_第2张图片

当然operator--我们只需要颠倒一下就可以~  这个算法需要自己自己走一走过程就会理解他为什么这么吊!!!!!更多红黑树的知识我们可以去看

一下我的上一个博客. 当然map和set肯定不仅仅只有红黑树版本的. 我们以后还会有hash版本的map和set.其实代码复用的原理都一样. 只不过后面

hash的模板结构嵌套更加复杂一点. 但是只要你理解了之后,自己也就会设计出来这些结构. Map和Set的结构我们只要好好地认识上面的图就可以好好

理解. 


你可能感兴趣的:(c++概念,C/C++经典面试题)