[C++ Advanced] 7. Use red-black tree to encapsulate set and map

Table of contents

foreword

1. Transform the red-black tree

1.1 Red-black tree iterator related

1.2 Red-black tree interface related

Two, set code

Three, map code


foreword

        set is the container of the K model, and map is the container of the KV model, but their underlying implementations are implemented by red-black trees, that is, red-black trees can be used to encapsulate set and map. Red-black trees have been discussed in previous chapters. Here I won't explain it.

        Next, transform the red-black tree, and use the transformed red-black tree to encapsulate set and map

1. Transform the red-black tree

The code used is the code implemented by the red-black tree in the previous chapter. After the transformation, the framework of the red-black tree is as follows:

#pragma once

enum Colour
{
	RED,
	BLACK,
};

template<class T>
struct RBTreeNode
{
	//构造函数
	RBTreeNode(const T& data)
		:_data(data)
		,_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_col(RED)
	{}

	//成员变量
	T _data;
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	Colour _col;
};

template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __RBTreeIterator<T, Ref, Ptr> Self;
	typedef __RBTreeIterator<T, T&, T*> iterator;

	//构造
	__RBTreeIterator(Node* node)
		:_node(node)
	{}

	// 普通迭代器的时候,他是拷贝构造
	// const迭代器的时候,他是构造,支持用普通迭代器构造const迭代器
	__RBTreeIterator(const iterator& s)
		:_node(s._node)
	{}

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

	Self& operator++()
	{
		if (_node->_right)
		{
			Node* min = _node->_right;
			while (min->_left)
			{
				min = min->_left;
			}

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

	Self& operator--()
	{
		if (_node->_left)
		{
			Node* max = _node->_left;
			while (max->_right)
			{
				max = max->_right;
			}

			_node = max;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	//成员变量
	Node* _node;
};

template<class K, class T, class KeyOfT>
class RBTree
{
	typedef RBTreeNode<T> Node;
public:
	typedef __RBTreeIterator<T, T&, T*> iterator;
	typedef __RBTreeIterator<T, const T&, const T*> const_iterator;

	iterator begin()
	{
		Node* left = _root;
		while (left->_left)
		{
			left = left->_left;
		}
		return iterator(left);// 使用匿名对象构造返回
	}

	const_iterator begin()const
	{
		Node* left = _root;
		while (left->_left)
		{
			left = left->_left;
		}
		return const_iterator(left);
	}

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

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

	//插入
	pair<iterator, bool> Insert(const T& data)
	{}
	//中序遍历
	void InOrder()
	{
		_InOrder(_root);
	}
	//检查红黑树特性
	bool IsBalance()
	{}
private:
	//检查每条路径的黑色节点是否相等 && 是否出现连续红色节点
	bool Check(Node* root, int blackNum, int ref)
	{}

	void _InOrder(Node* root)
	{}
	//左单旋
	void RotateL(Node* parent)
	{}
	//右单旋
	void RotateR(Node* parent)
	{}
private:
	Node* _root = nullptr;//缺省值
};

Too much code will not be posted, complete code link: code_c++/Set_and_Map/Set_and_Map/RBTree.h Maple_fylqh/code - Code Cloud - Open Source China (gitee.com) https://gitee.com/Maple_fylqh/code/blob/ master/code_c++/Set_and_Map/Set_and_Map/RBTree.h

Transforming the red-black tree is related to adding iterators to the red-black tree. This generic red-black tree can encapsulate set and map at the same time.

1.1 Red-black tree iterator related

The iterator template parameter has already been explained in the list simulation implementation, so I won’t explain it here. The template parameter T is the type of data stored in the red-black tree, which is explained below

template<class T, class Ref, class Ptr>

        The above iterator class only needs to provide a constructor . Copy construction and assignment do not need to be implemented by themselves. They can be generated by default. The current iterator is assigned to another iterator without deep copying. Shallow copying is fine. Copy construction Also; destructuring to release space is not managed by the iterator class, and the iterator class only needs to build an iterator object

The interfaces implemented by the red-black tree iterator above are:

    //构造
	__RBTreeIterator(Node* node)
		:_node(node)
	{}

	// 普通迭代器的时候,他是拷贝构造
	// const迭代器的时候,他是构造,支持用普通迭代器构造const迭代器
	__RBTreeIterator(const iterator& s)
		:_node(s._node)
	{}

    //解引用,获取数据
	Ref operator*(){}
    //返回数据的地址
	Ptr operator->(){}
    //比较两个迭代器是否不相等
	bool operator!=(const Self& s)const{}
    //比较两个迭代器是否相等
	bool operator==(const Self& s)const{}
    //前置++
	Self& operator++(){}
    //前置--
	Self& operator--(){}

Explain that iterators all support ordinary iterators to construct const iterators

The implementation is as follows, here it uses the characteristics of the constructor and the copy constructor, when the iterator passed in is an ordinary iterator: the parameter type is consistent with the copy construction parameter requirements, it is a copy construction; when the iterator passed in is const iterator, the parameter type does not match the copy constructor, if there is a difference in type, it becomes a constructor

typedef __RBTreeIterator<T, T&, T*> iterator;

// 普通迭代器的时候,他是拷贝构造
// const迭代器的时候,他是构造,支持用普通迭代器构造const迭代器
__RBTreeIterator(const iterator& s)
	:_node(s._node)
{}

Front ++ and pre-- are the focus of red-black tree iterator implementation

prefix ++

After the forward iterator of a node performs ++ operation, it should find the next node of the current node according to the sequence of in-order traversal of the red-black tree

As shown in the figure below, the first node passed in is 1, the next node of the iterator ++ is 6, and the ++ node again is 8, the iterator must achieve this effect to meet the demand

Ideas:

  1. Pass in a node, if the right side of the node is not empty, go to the right first , find the node with the smallest right node, this node is the next node of ++
  2. Pass in a node, if the right subtree of the node is empty, go to the ancestor, iterate and go up one by one, find the ancestor node whose child is the left of the father 

code show as below: 

Self& operator++()
{
	if (_node->_right)//传入节点右不为空,找右边最小节点
	{
		Node* min = _node->_right;
		while (min->_left)
		{
			min = min->_left;
		}

		_node = min;
	}
	else)//传入节点为空
	{
		Node* cur = _node;
		Node* parent = cur->_parent;
		while (parent && cur == parent->_right)
		{
			cur = cur->_parent;
			parent = parent->_parent;
		}
		_node = parent;
	}
	return *this;
}

Pre--

After the iterator of a node performs the -- operation, it is the previous node of the node found by the in-order traversal of the red-black tree 

As shown in the figure below, for example, the first node passed in is 27, the iterator -- the next node is 25, and again -- the node is 22, the iterator must achieve this effect to meet the demand

The idea of ​​-- is similar to the idea of ​​++, but it is reversed to find

Ideas:

  1. Pass in a node, if the left of the node is not empty, go to the left first , and find the node with the largest left node, which is the next node of --
  2. Pass in a node, if the left subtree of the node is empty, go to the ancestor, iterate and go up one by one, find the ancestor node whose child is the right of the father

code show as below:

Self& operator--()
{
	if (_node->_left)//传入节点左不为空就先往左边走,
	{
		Node* max = _node->_left;
		while (max->_right)
		{
			max = max->_right;
		}

		_node = max;
	}
	else//传入节点左为空
	{
		Node* cur = _node;
		Node* parent = cur->_parent;
		while (parent && cur == parent->_left)
		{
			cur = cur->_parent;
			parent = parent->_parent;
		}
		_node = parent;
	}
	return *this;
}

Others will not be introduced

1.2 Red-black tree interface related

template<class K, class T, class KeyOfT>
class RBTree
{
	typedef RBTreeNode<T> Node;
public:
	typedef __RBTreeIterator<T, T&, T*> iterator;
	typedef __RBTreeIterator<T, const T&, const T*> const_iterator;

	
private:
	Node* _root = nullptr;//缺省值
};

Explain the template parameters, in order to realize the generic RBTree:

  1. The first parameter of the red-black tree is K (Key), which is mainly used to find data;
  2. The second template parameter T of the red-black tree can identify whether it is a map or a set, because the parameters passed to T by set and map are different, the set passes the Key, and the map passes the key-value pair pair<K, V>;
  3. The third parameter of the red-black tree is KeyOfT, which is a functor, because T stored in the red-black tree may be K or pair<K, V>. Then how do we compare the size of the nodes when inserting? There is no problem with set. You can use T to compare directly, but map can’t. We need to take out the first of the pair<K, V> for comparison

Two, set code

#include "RBTree.h"

namespace fy
{
	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;

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

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

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

	void TestSet()
	{
		int arr[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
		set<int> s;
		for (auto& e : arr)
		{
			s.insert(e);
		}

		set<int>::iterator it = s.begin();
		while (it != s.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;

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

Three, map code

#include "RBTree.h"

namespace fy
{
	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<const K, pair<const K, V>, MapKeyOfT>::iterator iterator;
		typedef typename RBTree<const K, pair<const K, V>, MapKeyOfT>::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<iterator, bool> insert(const pair<const K, V>& kv)
		{
			return _t.Insert(kv);
		}

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

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

	void TestMap()
	{
		int arr[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
		map<int, int> m;
		for (auto& e : arr)
		{
			m.insert(make_pair(e, e));
		}

		map<int, int>::iterator it = m.begin();
		while(it != m.end())
		{
			it->second++;
			cout << it->first << "->" << it->second << endl;
			++it;
		}

		map<string, int> countMap;
		string str[] = { "苹果", "西瓜", "香蕉", "草莓", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
		for (auto& e : str)
		{
			countMap[e]++;
		}

		for (auto& kv : countMap)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
	}
}

The logic of this article is a bit unreasonable (difficult to write) dog, mainly for review

----------------I am the dividing line---------------

The article is over here, the next one will be updated soon

Guess you like

Origin blog.csdn.net/m0_64280701/article/details/129584330