红黑树迭代器及作为multi系列底层容器时的更改

本文为AVL树红黑树的额外文章,阅读本文至少需要阅读其中一篇,主要讲解红黑树的iterator,以及作为multiset,multimap底层容器时的更改,红黑树插入删除的旋转平衡请见RedBlackTree 红黑树


文章目录

文章目录

对红黑树节点的改造

对红黑树模板参数的改造

class T        

class K

class Compare

iterator的实现

自增

自减

 insert

红黑树私有成员(非全部,仅放出变量的以及重命名)

插入逻辑抽离 

插入函数 

相同key值元素的插入

find

erase

set & multiset 

map & multimap 

总结


对红黑树节点的改造

enum RBColor { RED, BLACK };
template<class T>
struct RBTreeNode
{
private:
	typedef T data_type;
	typedef RBTreeNode<data_type> self;
	typedef self* self_pointer;
public:
	explicit RBTreeNode(const data_type& data)
		: _data(data)
	{}
	explicit RBTreeNode(data_type&& data = data_type())  //右值移动构造_data
		: _data(std::forward<data_type>(data))
	{}
	template<class... Args>
	RBTreeNode(Args&&... args)    //可变参数模板构造_data(实质上,上面两种构造都可以被改构造替代,出于阅读性建议保留)
		: _data(args...)
	{}


	void swap(self & node)
	{
		std::swap(_data, node._data);
		std::swap(_color, node._color);
		std::swap(_pLeft, node._pLeft);
		std::swap(_pRight, node._pRight);
		std::swap(_pParent, node._pParent);
	}

	data_type _data = data_type();
	RBColor _color = RBColor::RED;
	self_pointer _pLeft = nullptr;
	self_pointer _pRight = nullptr;
	self_pointer _pParent = nullptr;
};

对比先前红黑树的节点

         可以看到,先前红黑树节点中存储的是一个pair,但改造后的节点中只存储了一个模板参数T(data_type),这是为了适配map以及set进行的改造。
        先前存储pair时,我们使用pair的first作为key,second作为value,组成键值对,但这种写法是不是就直接固定了,假如用户想要用pair的second作为key,first作为value时,就只能更改红黑树的原码了,这是非常不好的一种情况。

        所以将其改为模板T类型,那怎么拿取key进行比较
        
使用仿函数,将仿函数作为拿去key的方法,传入红黑树的模板参数中,那么只需在红黑树中将该仿函数实例化,使用其对T类型中的key进行拿取
        当T为单个类型时,传入key返回key(在set中使用的仿函数)

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

        当T为pair类型时,传入pair返回pair的first(在map中使用的仿函数)

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

         那么,同理,无论是什么类型,我们都可以使用KeyOfValue仿函数对其进行取key。


对红黑树模板参数的改造

template<class K, class T, class KeyOfValue, class Compare = less<K>>
class RBTree;

class T        

        根据上述中对红黑树节点的改造,可以得知,在红黑树的插入删除过程中,需要KeyOfValue仿函数对T类型取key进行比较,故在模板参数中添加该模板。

class K

        可能有些读者会疑惑,为什么还需要class K模板,已经有了T模板作为红黑树节点中存储的值,也有KeyOfValue可以取到T类型中的key,那么模板类型K是不是有点多余?
         但有一点需要考虑的是,find函数,find函数的原型:

iterator find(const K& key)

         假设我们将模板类型K去掉之后,find的参数我们就无法构造了。

class Compare

        使用STL库中的map与set时,可以看出,库中的容器允许传入less等仿函数,对其中数据的排序方式进行调整。
        所以在构造红黑树作为容器的底层结构时也需要支持,但一个Compare怎么做到对key值比较大于,小于,等于操作?
        使用一个比较符号完成大于等于小于比较

if (a > b)      //a > b
    { ... }
else if (b > a) //a < b
    { ... }
else            //a == b
    { ... }

iterator的实现

        先讲解一个iterator之后,再去讲解insert操作和erase操作的更改
        红黑树其本质也是一颗二叉搜索树,只不过增加了一些规则限制,使得红黑树可以完成高度相对平衡,高度可以维持在logN,所以当对红黑树进行中序遍历的时候,就可以得到一个有序序列,所以使用iterator对红黑树的遍历也是中序遍历。
        那么根据二叉搜索树中序遍历的性质,可以得出以下结论
        1.begin一定是最左侧节点(最小节点)
        2.end - 1 一定是最右侧节点(最大节点)
        3.自增时,下一个节点一定大于/等于自身(multi系列允许存储相同key值的元素)
        4.自减时,下一个节点一定小于/等于自身
        5.一个节点的左侧节点一定小于/等于自身,右侧节点一定大于/等于自身

自增

        首先应该判断是否有右子树,若有右子树,则下一个节点,一定是右子树中最小的节点。
        若没有右子树,则该节点一定为以自身为根的子树中最大的节点,以自身为根的子树中序遍历完成,向上查找,寻找比自己大的双亲节点
                若自身为父亲的左子树,则父亲节点即为下一个节点。
                若自身为父亲的右子树,则父亲节点小于等于自身,根据中序遍历的性质,一定先遍历了父亲节点后再遍历了自身,则继续向上查找。

自减

        首先应该判断是否有左子树,若有左子树,则下一个节点,一定是左子树中最大的节点。
        若没有左子树,则该节点一定为以自身为根的子树中最小的节点,以自身为根的子树中序遍历开始,向上查找,寻找比自己小的双亲节点
                若自身为父亲的右子树,则父亲节点即为下一个节点。
                若自身为父亲的左子树,则父亲节点大于等于自身,根据中序遍历的性质,一定先遍历了自身后再遍历双亲节点,则继续向上查找。

        但这样还有一个最大的问题,该节点为起始节点时自减或者最后节点自增的话,程序就会崩掉,而且end也不能使用nullptr构造,若使用nullptr构造end的话,end--就无法判断了。
        所以,需要在红黑树中添加一个_pHead头节点,其左指针指向最左侧节点,右指针指向最右侧节点就可以了。end就可以使用_pHead构造。(树中节点无需指向_pHead,只需在class iterator中也存储一个_pHead,这样就不用担心增加头节点后会导致红黑树性质被破坏)

	template<class TreeNode, class K, class T, class KeyOfValue, class Compare>
	struct __tree_const_iterator
	{
	private:
		typedef TreeNode Node;
		typedef Node* NodePtr;
		typedef __tree_const_iterator<TreeNode, K, T, KeyOfValue, Compare> self;

		friend class AVLTree<K, T, KeyOfValue, Compare>;        //erase操作时需要用到iterator中的_node,为了不破坏封装,使用友元
		friend class RBTree<K, T, KeyOfValue, Compare>;

		NodePtr _node = nullptr;
		NodePtr _head = nullptr;
	public:
		typedef T data_type;
		typedef const T& reference_type;
		typedef const T* pointer_type;

		explicit __tree_const_iterator(NodePtr node = nullptr, NodePtr head = nullptr)
			: _node(node)
			, _head(head)
		{}

		reference_type operator*() const {
			return _node->_data;
		}
		pointer_type operator->() const {
			return &(_node->_data);
		}

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

		self& operator++() {
			if (_node == _head)		//当_node位置为_pHead时,下一个位置变为最小节点
				_node = _head->_pLeft;
			else if (_node == _head->_pRight)	//当_node为最大节点时,下一个位置为_pHead
				_node = _head;
			else if (_node->_pRight)	//若右子树存在,则下一个位置为右子树最小节点
			{
				_node = _node->_pRight;
				while (_node->_pLeft)
					_node = _node->_pLeft;
			}
			else	//若右子树不存在,则下一个节点为第一个非右子树的节点的双亲结点
			{
				NodePtr parent = _node->_pParent;
				while (_node == parent->_pRight)	//找到下一个节点
				{
					_node = parent;
					parent = _node->_pParent;
				}
				_node = parent;
			}
			return *this;
		}
		self operator++(int) {
			self it = *this;
			++(*this);
			return it;
		}
		self& operator--() {
			if (_node == _head->_pLeft)		//当_node为起始位置时,变为_pHead
				_node = _head;
			else if (_node == _head)		//当_node位置为_pHead时,上一个位置为最大节点
				_node = _node->_pRight;
			else if (_node->_pLeft)	//若左子树存在,则上一个位置为左子树中最大节点
			{
				_node = _node->_pLeft;
				while (_node->_pRight)
					_node = _node->_pRight;
			}
			else	//若左子树不存在,则上一个位置为第一个非左子树的节点的双亲结点
			{
				NodePtr parent = _node->_pParent;
				while (_node == parent->_pLeft)
				{
					_node = parent;
					parent = _node->_pParent;
				}
				_node = parent;
			}
			return *this;
		}
		self operator--(int) {
			self it = *this;
			--(*this);
			return it;
		}

		void swap(self& it) {
			std::swap(_node, it._node);
			std::swap(_head, it._head);
		}
	};

	template<class TreeNode, class K, class T, class KeyOfValue, class Compare>
	struct __tree_iterator : public __tree_const_iterator<TreeNode, K, T, KeyOfValue, Compare>
	{    //public继承const_iterator,使得iterator可以转化为const_iterator
	private:
		typedef TreeNode Node;
		typedef Node* NodePtr;
		typedef __tree_iterator<TreeNode, K, T, KeyOfValue, Compare> self;
		typedef __tree_const_iterator<TreeNode, K, T, KeyOfValue, Compare> base_class;

		friend class AVLTree<K, T, KeyOfValue, Compare>;
		friend class RBTree<K, T, KeyOfValue, Compare>;
	public:
		typedef T data_type;
		typedef data_type& reference_type;
		typedef data_type* pointer_type;

		explicit __tree_iterator(NodePtr node = nullptr, NodePtr head = nullptr)
			: base_class(node, head)
		{}

		reference_type operator*() const {
			return const_cast<reference_type>(base_class::operator*());
		}
		pointer_type operator->() const {
			return (&operator*());
		}

		self& operator++() {
			base_class::operator++();
			return *this;
		}
		self operator++(int) {
			self ret = *this;
			base_class::operator++();
			return ret;
		}
		self& operator--() {
			base_class::operator--();
			return *this;
		}
		self operator--(int) {
			self ret = *this;
			base_class::operator--();
			return ret;
		}
	};

 insert

        插入操作中需要增加emplace以及右值插入的版本,但普通的insert与右值insert和emplace的插入代码除去构造节点时的不同,其余逻辑完全相同,所以我们需要将整体的逻辑抽离出去,将构造好的节点传入即可完成复用。

红黑树私有成员(非全部,仅放出变量的以及重命名)

template<class K, class T, class KeyOfValue, class Compare = less<K>>
class RBTree
{
    //类型重命名,使用重命名的话,AVL树的接口什么的基本完全一样,改造AVL树就只用直接copy就行
	typedef T data_type;
	typedef data_type& reference;
	typedef const data_type& const_reference;
	typedef data_type* pointer;
	typedef const data_type* const_pointer;

	typedef RBTreeNode<data_type> Node;
	typedef Node* NodePtr;
	typedef RBTree<K, data_type, KeyOfValue, Compare> self;
    
    const NodePtr get_minNode()
    {
	    NodePtr cur = _root;
	    while (cur && cur->_pLeft != nullptr)
	       	cur = cur->_pLeft;
	    return cur;
    }
    const NodePtr get_maxNode()
    {
    	NodePtr cur = _root;
	    while (cur && cur->_pRight != nullptr)
	    	cur = cur->_pRight;
	    return cur;
    }
    void keep_HeadNode()
    {
    	_pHead->_pLeft = get_minNode();
    	_pHead->_pRight = get_maxNode();
    }

    NodePtr _root;     //根节点
    NodePtr _pHead;    //头节点
}

插入逻辑抽离 

pair<NodePtr*, NodePtr> _insert_find_position(const K& key)
{
	KeyOfValue kov = KeyOfValue();
	Compare cmp = Compare();
	NodePtr parent = nullptr;
	NodePtr* ptr = &_root;
	for (NodePtr cur = *ptr; cur != nullptr; cur = *ptr)
	{
		if (cmp(key, kov(cur->_data)))
		{
			parent = cur;
			ptr = &cur->_pLeft;
		}
		else if (cmp(kov(cur->_data), key))
		{
			parent = cur;
			ptr = &cur->_pRight;
		}
		else
			break;
	}
	return make_pair(ptr, parent);
}
void _insert_keep_balance(NodePtr cur, NodePtr parent)
{
	while (cur != _root && parent->_color == RBColor::RED)
	{
		NodePtr grandparent = parent->_pParent;
		NodePtr uncle = grandparent->_pLeft == parent ? grandparent->_pRight : grandparent->_pLeft;

		if (uncle == nullptr || uncle->_color == RBColor::BLACK)
		{	//cur为红,parent为红,grandparent为黑,uncle不存在/存在且为黑
			if (parent == grandparent->_pLeft)
			{
				if (cur == parent->_pRight)		//当parent在grandparent的左侧而cur在parent的右侧时,先对parent进行左旋
					parent = rotate_left(parent);
				rotate_right(grandparent);		//再对grandparent进行右旋
			}
			else
			{
				if (cur == parent->_pLeft)		//当parent在grandparent的右侧而cur在parent的左侧时,先对parent进行右旋
					parent = rotate_right(parent);
				rotate_left(grandparent);		//再对grandparent进行左旋
			}
			parent->_color = RBColor::BLACK;
			grandparent->_color = RBColor::RED;
		}
		else if (uncle->_color == RBColor::RED)
		{	//cur为红,parent为红,grandparent为黑,uncle存在且为红
			parent->_color = uncle->_color = RBColor::BLACK;
			grandparent->_color = RBColor::RED;
			cur = grandparent;
			parent = cur->_pParent;
		}
	}
	_root->_color = RBColor::BLACK;
}
pair<iterator, bool> _insert(NodePtr node)
{
	KeyOfValue kov = KeyOfValue();
	pair<NodePtr*, NodePtr> ret = _insert_find_position(kov(node->_data));
	if (*(ret.first) != nullptr)
	{
		delete node;
		return make_pair(iterator(*(ret.first), _pHead), false);
	}
	else
	{
		node->_pParent = ret.second;
		*(ret.first) = node;
	}
	_insert_keep_balance(node, ret.second);
	keep_HeadNode();
	return make_pair(iterator(node, _pHead), true);
}

插入函数 

pair<iterator, bool> insert(const data_type& data)
{
	return _insert(new Node(data));
}
pair<iterator, bool> insert(data_type&& data)
{
	return _insert(new Node(qyc::forward<data_type>(data)));
}
template<class... Args>
pair<iterator, bool> emplace(Args&&... args)
{
	return _insert(new Node(args...));
}
template<class InputIterator>
void insert_range(InputIterator first, InputIterator last)
{
	while (first != last)
	{
		insert(*first);
		++first;
	}
}

相同key值元素的插入

pair<iterator, bool> _insert_equal(NodePtr node)
{
	KeyOfValue kov = KeyOfValue();
	pair<NodePtr*, NodePtr> ret = _insert_equal_find_position(kov(node->_data));
	node->_pParent = ret.second;
	*(ret.first) = node;
	_insert_keep_balance(node, ret.second);
	keep_HeadNode();
	return make_pair(iterator(node, _pHead), true);
}
pair<NodePtr*, NodePtr> _insert_equal_find_position(const K& key)
{
	KeyOfValue kov = KeyOfValue();
	Compare cmp = Compare();
	NodePtr parent = nullptr;
	NodePtr* ptr = &_root;
	for (NodePtr cur = *ptr; cur != nullptr; cur = *ptr)
	{
		if (cmp(key, kov(cur->_data)))
		{
			parent = cur;
			ptr = &cur->_pLeft;
		}
		else	//key相等放右边,不绝对(旋转后位置会改变)
		{
			parent = cur;
			ptr = &cur->_pRight;
		}
	}
	return make_pair(ptr, parent);
}

pair<iterator, bool> insert_equal(const data_type& data)
{
	return _insert_equal(new Node(data));
}
pair<iterator, bool> insert_equal(data_type&& data)
{
	return _insert_equal(new Node(qyc::forward<data_type>(data)));
}
template<class... Args>
pair<iterator, bool> emplace_equal(Args&&... args)
{
	return _insert_equal(new Node(args...));
}
template<class InputIterator>
void insert_equal_range(InputIterator first, InputIterator last)
{
	while (first != last)
	{
		insert_equal(*first);
		++first;
	}
}

find

        find本身没有什么好讲解的,但当插入了相同key值元素时,find就变得稍微复杂了一些,看库中实现的find,当插入了相同key值时,返回的时中序遍历时第一个得到的元素。
        因此可以从begin开始遍历直到找到,但如此效率便失去了
        所以改进了一下,首先按照正常的搜索进行查找,找到之后,将该节点构造为iterator,--it,直到与要查找的值不同为止,再++it即得到了目标元素。(需要先仔细将iterator理解清楚才可,注意begin - 1为end,end - 1为最右侧节点)

		iterator find(const K& key)
		{
			KeyOfValue kov = KeyOfValue();
			Compare cmp = Compare();
			for (NodePtr cur = _root; cur != nullptr; )
			{
				if (cmp(key, kov(cur->_data)))
					cur = cur->_pLeft;
				else if (cmp(kov(cur->_data), key))
					cur = cur->_pRight;
				else
				{
					iterator prev(cur, _pHead);
					for (iterator it(cur, _pHead); it != end() && !cmp(kov(*it), key) && !cmp(key, kov(*it)); --it)
						prev = it;
					return prev;
				}
			}
			return end();
		}
		const_iterator find(const K& key) const
		{
			KeyOfValue kov = KeyOfValue();
			Compare cmp = Compare();
			for (NodePtr cur = _root; cur != nullptr; )
			{
				if (cmp(key, kov(cur->_data)))
					cur = cur->_pLeft;
				else if (cmp(kov(cur->_data), key))
					cur = cur->_pRight;
				else
				{
					const_iterator prev(cur, _pHead);
					for (const_iterator it(cur, _pHead); it != end() && !cmp(kov(*it), key) && !cmp(key, kov(*it)); --it)
						prev = it;
					return prev;
				}
			}
			return end();
		}

erase

        erase直接接收find的值就行,删除,判断性质,保持平衡

bool erase(const K& key)
{
	iterator result = find(key);
	if (result != end())
	{
		NodePtr cur = result._node;    //需要将RBTree声明为__tree_iterator的友元
		NodePtr parent = cur->_pParent;
		NodePtr* ptr = parent == nullptr ? &_root : (cur == parent->_pLeft ? &parent->_pLeft : &parent->_pRight);
		_erase(ptr);
		keep_HeadNode();
		return true;
	}
	else
		return false;
}

set & multiset 

template<class K, class Compare = less<K>>
class set
{
	struct SetKeyOfValue
	{
		const K& operator()(const K& key)
		{
			return key;
		}
	};
	typedef RBTree<K, K, SetKeyOfValue, Compare> tree_type;
	typedef set<K, Compare> self;
public:
    //_tree接口复用...(使用insert)
ptivate:
    tree_type _tree;
};

template<class K, class Compare = less<K>>
class multiset
{
	struct SetKeyOfValue
	{
		const K& operator()(const K& key)
		{
			return key;
		}
	};
	typedef RBTree<K, K, SetKeyOfValue, Compare> tree_type;
	typedef multiset<K, Compare> self;
public:
    //_tree接口复用...(使用insert_equal)
ptivate:
    tree_type _tree;
};

map & multimap 

template<class K, class Compare = less<K>>
class map
{
	typedef pair<K, V> data_type;
    struct MapKeyOfValue
    {
	    const K& operator()(const data_type& kv)
	    {
	    	return kv.first;
	    }
    };
    typedef RBTree<K, data_type, MapKeyOfValue, Compare> tree_type;
    typedef map<K, V, Compare> self;
public:
    //_tree接口复用...(使用insert)
ptivate:
    tree_type _tree;
};

template<class K, class Compare = less<K>>
class multimap
{
	typedef pair<K, V> data_type;
    struct MapKeyOfValue
    {
	    const K& operator()(const data_type& kv)
	    {
	    	return kv.first;
	    }
    };
    typedef RBTree<K, data_type, MapKeyOfValue, Compare> tree_type;
    typedef multimap<K, V, Compare> self;
public:
    //_tree接口复用...(使用insert_equal)
ptivate:
    tree_type _tree;
};

总结

        当然也可以使用其他的方式实现,都可以,本文也不过是一个实现而已(而且也比较挫),也并非为标准的实现,理解大致的思路逻辑就好啊。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值