红黑树封装map和set

1.map和set

map与set的底层是红黑树,直接在map与set中封装一棵红黑树,然后包装其接口。

2.改造红黑树

如果我们要将红黑树拿去封装map和set的话,首先需要增加迭代器,便于map与set的遍历。

template 
	struct __RBTreeIterator
	{
		typedef RBTreeNode Node;
		Node* _node;

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

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

		typedef __RBTreeIterator Self;

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

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

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

		Self& operator++()
		{
			if (_node->_right)
			{
				Node* subLeft = _node->_right;
				while (subLeft->_left)
				{
					subLeft = subLeft->_left;
				}
				_node = subLeft;
			}
			else
			{
				Node* cur = _node;
				Node* parent = cur->_parent;
				while (parent && cur == parent->_right)
				{
					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
			{
				Node* cur = _node;
				Node* parent = cur->_parent;
				while (parent && cur == parent->_left)
				{
					cur = parent;
					parent = parent->_parent;
				}

				_node = parent;
			}

			return *this;
		}
	};
begin()与end()
STL明确规定,begin()与end()代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,可以得到一个有序的序列,因此:begin()可以放在红黑树中最小节点(即最左侧节点)的位 end()放在最大节点(最右侧节点)的下一个位置。

2.1 operator++与--

operator++分为有右子树和没有右子树两种情况。顺序是中序遍历,左根右。

右不为空,下一个就是右子树的最左节点。

右为空,沿着到根的路径,找孩子是父亲左的那个祖先。

红黑树封装map和set_第1张图片

 operator--则相反,分为左子树为空和左子树不为空两种情况。

左子树不为空,去找左子树的最右节点。

左子树为空,沿着到根的路径,找孩子是父亲右的那个祖先。

与中序遍历相反,右根左。

2.2 begin与end

template 
	class RBTree
	{
		typedef RBTreeNode Node;
	public:
		typedef __RBTreeIterator  Iterator;
		typedef __RBTreeIterator  const_Iterator;
		Iterator begin()
		{
			Node* cur = _root;
			while (cur && cur->_left)
			{
				cur = cur->_left;
			}
			return Iterator(cur);
		}

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

begin直接是左子树的最左节点,end直接给空指针。

3.map

#pragma once
#include "RBTree.h"

namespace L
{
	template 
	class map
	{
		struct MapKeyofT
		{
			const K& operator()(const pair& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename RBTree, MapKeyofT>::Iterator Iterator;
		Iterator begin()
		{
			return _t.begin();
		}

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

		pair Insert(const pair& kv)
		{
			return _t.Insert(kv);
		}

		V& operator[](const K& key)
		{
			pair ret = _t.Insert(make_pair(key, V()));
			return ret.first->second;
		}

	private:
		RBTree, MapKeyofT> _t;
	};

直接套用红黑树的接口。map的插入函数的返回值需要设计成pair的形式,以便于operator[]能实现插入+修改Val的功能。

4.set

#pragma once
#include "RBTree.h"

namespace L
{
	template 
	class set
	{
		struct SetKeyofT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename RBTree::const_Iterator Iterator;
		typedef typename RBTree::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();
		}

		pair Insert(const K& key)
		{
			return _t.Insert(key);
		}
	private:
		RBTree _t;
	};

5.迭代器隐式类型转换问题

由于set中不允许修改key,所以set的迭代器都是设计成const迭代器。

typedef typename RBTree::const_Iterator Iterator;
typedef typename RBTree::const_Iterator const_Iterator;

而set在调用红黑树的begin时,返回的是一个普通迭代器,而普通迭代器无法转换成const迭代器。那把红黑树里的迭代器都设计成const迭代器行不行呢?答案是不行,因为map里需要普通迭代器来修改val。

typedef typename RBTree, MapKeyofT>::Iterator Iterator;

为此我们需要在红黑树迭代器里设计一个特殊的构造函数。

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

当传进来的是普通迭代器时,这个构造函数就是拷贝构造。

当传进来的是const迭代器时,这个构造函数就是一个支持用普通迭代器来构造const迭代器的构造函数。这个构造函数是一个单参数的隐式类型转换构造函数,把普通迭代器转换成了模板实例化的const迭代器对象本身。

你可能感兴趣的:(C++学习,算法,数据结构)