[C++] 맵과 세트의 시뮬레이션 구현

여기에 이미지 설명을 삽입하세요.

마음에 드셨다면 좋아요, 즐겨찾기, 팔로우까지!여기에 이미지 설명을 삽입하세요.

이전 C++ 블로그에서는 이진 검색 트리, AVL 트리, 레드-블랙 트리에 대해 이야기했습니다.
오늘은 레드-블랙 트리를 사용하여 맵과 세트를 시뮬레이션하겠습니다.

이제 문제가 생겼습니다. 레드-블랙 트리가 주어지면 맵과 설정을 시뮬레이션하기 위해 이를 어떻게 사용해야 합니까? 단, 맵은 KV 모델이고, 세트는 K 모델입니다. 빨간색과 검은색 트리에 대해 별도로 수정해야 합니까?

잘 모르겠다면 Curry가 이를 어떻게 구현하는지 살펴보는 것이 좋습니다.
여기에 이미지 설명을 삽입하세요.

위 사진은 각각 우리의 map과 set인데, 주된 이유는 red-black tree에 전달된 두 번째 템플릿 매개변수가 value_type인데 map의 value_type은 pair<const Key, T>이고 set의 value_type은 Key이기 때문입니다. .
우리 모두는 주어진 매개변수가 다르기 때문에 동일한 템플릿이 다른 클래스를 인스턴스화할 수 있다는 것을 알고 있습니다.

따라서 두 번째 템플릿 매개변수에 전달된 매개변수가 다르기 때문에 KV 모델과 K 모델에 대한 문제가 해결될 수 있으므로 맵과 세트의 하위 레이어도 동일한 레드-블랙 트리 템플릿을 사용할 수 있습니다.

레드-블랙 트리 라이브러리를 다시 살펴보고 이것이 왜 이와 같은 매개변수를 제공하는지 알게 될 것입니다.
여기에 이미지 설명을 삽입하세요.
레드-블랙 트리의 두 번째 템플릿 매개변수는 맵(KV 모델)인지 세트(K 모델)인지 결정합니다.

그러면 왜 첫 번째 매개변수인 Key를 전달해야 할까요?
여기에 이미지 설명을 삽입하세요.
여기서 가장 큰 이유는 find가 존재하기 때문입니다. 레드-블랙 트리의 관점에서 보면 무엇을 전달하는지 알 수 없습니다. 예를 들어 insert입니다. 상위 레이어가 무엇을 전달하든 저는 다음과 같은 노드를 삽입합니다. 마찬가지로 find는 상위 레이어가 전달하는 모든 값을 누를 것입니다. 찾을 값 유형은 무엇입니까? 하지만 K 모델이든 KV 모델이든 KV 모델이 값을 변경할 수 있더라도 K 대신 V가 변경됩니다. 따라서 K를 사용하여 검색할 수 있습니다.

다음으로 레드-블랙 트리를 사용하여 맵을 시뮬레이션하고 설정합니다.

enum Coloer
{
    
    
	RED,
	BLACK
};

template<class T>
struct RBTreeNode
{
    
    
	T _data;
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	Coloer _col;

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

template<class K, class T>
class RBTree
{
    
    
	typedef RBTreeNode<T> Node;
public:	
	bool Insert(const T& data)
	{
    
    
		if (_root == nullptr)
		{
    
    
			_root = new Node(data);
			_root->_col = BLACK;
			return true;
		}


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

		cur = new Node(data);
		cur->_col = RED;
		if (cur->_kv.first < parent->_kv.first)
		{
    
    
			parent->_left = cur;
			cur->_parent = parent;
		}
		else
		{
    
    
			parent->_right = cur;
			cur->_parent = parent;
		}

		//调整
		while (parent && parent->_col == RED)
		{
    
    
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
    
    
				Node* uncle = grandfather->_right;
				if (uncle && uncle->_col == RED)//情况一
				{
    
    
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					cur = grandfather;
					//如果是整树,就可能发生野指针,
					parent = cur->_parent;
				}
				else
				{
    
    
					if (cur == parent->_left)//情况二
					{
    
    
						RotaleR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else//情况三
					{
    
    
						RotaleL(parent);
						RotaleR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
			else
			{
    
    
				Node* uncle = grandfather->_left;
				if (uncle && uncle->_col == RED)
				{
    
    
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
    
    
					if (cur == parent->_right)
					{
    
    
						RotaleL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
    
    
						RotaleR(parent);
						RotaleL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}

		}
		_root->_col = BLACK;
		return true;
	}

	void RotaleL(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 == nullptr)
		{
    
    
			_root = subR;
			_root->_parent = nullptr;
		}
		else
		{
    
    
			if (parent == ppNode->_left)
			{
    
    
				ppNode->_left = subR;
			}
			else
			{
    
    
				ppNode->_right = subR;
			}
			subR->_parent = ppNode;
		}
	}

	void RotaleR(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 == nullptr)
		{
    
    
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
    
    
			if (parent == ppNode->_left)
			{
    
    
				ppNode->_left = subL;
			}
			else
			{
    
    
				ppNode->_right = subL;
			}
			subL->_parent = ppNode;
		}
	}

private:
	Node* _root = nullptr;
};

편의상 매개변수 전달 시 라이브러리처럼 주어지지 않습니다.

//Map.h
namespace bit
{
    
    
	template<class K,class V>
	class map
	{
    
    
	private:
		RBTree<K, pair<const K, V>> _t;
	};
}
//Set.h
namespace bit
{
    
    
	template<class K>
	class set
	{
    
    
	private:
		RBTree<K, K> _t;
	};
}

다음으로 삽입을 구현합니다.

모의 구현 삽입

하지만 구현하기 전에 문제가 있습니다.삽입은 비교입니다.
Map은 쌍을 제공하고, 집합은 키를 제공하며 쌍의 비교 규칙은 첫 번째가 다음보다 작지 않고 두 번째를 비교하는 것입니다(첫 번째는 쌍의 첫 번째 매개변수 값이고 두 번째는 두 번째 매개변수의 값입니다). 키를 비교하고 싶습니다. 그래서 뭐 할까?
여기에 이미지 설명을 삽입하세요.
그런 다음 매핑과 설정만 비교 키가 되도록 변경하는 방법을 찾을 것입니다. 물론 여기서는 쌍 비교를 다시 작성하는 것이 아니라 각각 map과 set에 대한 펑터를 작성하는 것, 즉 세 번째 템플릿 매개변수를 레드-블랙 트리에 전달하는 것입니다.
여기에 이미지 설명을 삽입하세요.

//map.h
	template<class K,class V>
	class map
	{
    
    
		struct MapKeyofT
		{
    
    
			const K& operator()(const pair<const K, V>& kv)
			{
    
    
				return kv.first;
			}
		};
	private:
		RBTree<K, pair<const K, V>,MapKeyofT> _t;
	};
//set.h
	template<class K>
	class set
	{
    
    
		struct SetKeyofT
		{
    
    
			const K& operator()(const K& key)
			{
    
    
				return key;
			}
		};
	private:
		RBTree<K, K,SetKeyofT> _t;
	};
	
//RBTree.h
bool Insert(const T& data)
	{
    
    
		if (_root == nullptr)
		{
    
    
			_root = new Node(data);
			_root->_col = BLACK;
			return true;
		}
		KeyofT kot;
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
    
    
			//比较都要修改
			if (kot(cur->_data) < kot(data))
			{
    
    
				parent = cur;
				cur = cur->_right;
			}
			else if (kot(cur->_data) > kot(data))
			{
    
    
				parent = cur;
				cur = cur->_left;
			}
			else
			{
    
    
				return false;
			}
		}

		cur = new Node(data);
		cur->_col = RED;
		if (kot(cur->_data) < kot(parent->_data))
		{
    
    
			parent->_left = cur;
			cur->_parent = parent;
		}
		else
		{
    
    
			parent->_right = cur;
			cur->_parent = parent;
		}

		//调整
		while (parent && parent->_col == RED)
		{
    
    
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
    
    
				Node* uncle = grandfather->_right;
				if (uncle && uncle->_col == RED)//情况一
				{
    
    
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					cur = grandfather;
					//如果是整树,就可能发生野指针,
					parent = cur->_parent;
				}
				else
				{
    
    
					if (cur == parent->_left)//情况二
					{
    
    
						RotaleR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else//情况三
					{
    
    
						RotaleL(parent);
						RotaleR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
			else
			{
    
    
				Node* uncle = grandfather->_left;
				if (uncle && uncle->_col == RED)
				{
    
    
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
    
    
					if (cur == parent->_right)
					{
    
    
						RotaleL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
    
    
						RotaleR(parent);
						RotaleL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}

		}
		_root->_col = BLACK;
		return true;
	}

레드-블랙 트리 삽입이 구현되었습니다. map을 호출하고 설정하면 됩니다.

//map.h
template<class K,class V>
	class map
	{
    
    
		struct MapKeyofT
		{
    
    
			const K& operator()(const pair<const K, V>& kv)
			{
    
    
				return kv.first;
			}
	};
	public:
		bool Insert(const pair<const K, V>& kv)
		{
    
    
			return _t.Insert(kv);
		}

	private:
		RBTree<K, pair<const K, V>,MapKeyofT> _t;
	};
	
//set.h
template<class K>
	class set
	{
    
    
		struct SetKeyofT
		{
    
    
			const K& operator()(const K& key)
			{
    
    
				return key;
			}
		};
	public:
		bool Insert(const K& key)
		{
    
    
			return _t.Insert(key);
		}
	private:
		RBTree<K, K,SetKeyofT> _t;
	};

삽입에 대한 이야기는 여기서 그만하고, 아직 문제가 남아있는데, 반복자에 대해 알아보고 다시 해결하도록 하겠습니다.

순방향 반복기 시뮬레이션 구현 + 전체 구현 삽입

먼저 레드-블랙 트리 반복자를 구현한 다음 맵과 세트를 호출합니다.

레드-블랙 트리 반복자와 리스트 반복자의 구현 방법은 동일하며, 네이티브 포인터로는 문제를 해결할 수 없으므로 + 연산자 오버로딩을 캡슐화하여 필요한 반복자를 구현합니다.

여기서 한 가지 추가 사항은 레드-블랙 트리의 구조가 Curry가 제공한 구조와 다르다는 것입니다.
Curry의 레드-블랙 트리에는 헤드 노드가 있으며, 왼쪽 포인터는 맨 왼쪽을 가리키고 오른쪽 포인터는 맨 오른쪽을 가리킵니다.
이는 반복자가 순서대로 되어 있고 라이브러리의 디자인을 통해 반복자가 시작하고 끝나는 위치를 쉽게 찾을 수 있기 때문입니다.
여기에 이미지 설명을 삽입하세요.
그리고 우리의 레드-블랙 트리에는 헤드 노드가 없습니다. 그냥 평범한 레드-블랙 트리입니다. 반복자의 후속 구현은 아래 구조에 따라 구현됩니다.
여기에 이미지 설명을 삽입하세요.

목록 반복자 시뮬레이션 구현의 기초를 고려하여 먼저 간단한 반복자를 구현한 후 단계별로 추가합니다.

template<class T>
class _RBTreeIterator
{
    
    
public:
	typedef RBTreeNode<T> Node;
	Node* _node;

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

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

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

};

template<class K, class T,class KeyofT>
class RBTree
{
    
    
	typedef RBTreeNode<T> Node;
public:
	typedef _RBTreeIterator<T> iterator;
	/。。。。。
}

현재 반복자를 사용하려면 여전히 ++ 연산자 오버로드가 필요합니다.
그렇다면 레드-블랙 트리는 이 ++를 어떻게 구현해야 할까요?

반복자는 중간 순서이며 아래 그림에 따르면 첫 번째 위치는 5가 되어야 합니다.
여기에 이미지 설명을 삽입하세요.
왼쪽 하위 트리, 루트, 오른쪽 하위 트리의 중간 순서에 따르면 첫 번째 위치는 5입니다. 왼쪽 하위 트리와 루트가 모두 탐색되었습니다. 다음은 오른쪽 하위 트리입니다. 따라서 우리가 지금 직면하고 있는 상황은 두 가지 상황입니다. 오른쪽 하위 트리가 존재하거나 비어 있습니다.

오른쪽 하위 트리는 비어 있지 않으며 위치 8에 있습니다.
여기에 이미지 설명을 삽입하세요.
다음 위치는 10번 위치입니다. 즉, 다음 오른쪽 하위 트리의 가장 왼쪽 노드입니다.

오른쪽은 비어있고 7번 위치에 있습니다.
여기에 이미지 설명을 삽입하세요.
즉, 5번과 6번을 방문했고, 다음 위치에서는 8번을 방문할 차례입니다. 조상에 접근하기 위해 직접 글을 쓸 수 있나요?
그렇다면 7 아래에 또 다른 위치가 있다면 그것은 조상의 자리가 아닐까? 그러니 조상님만 방문할 수는 없습니다.

이렇게 생각해보세요. 왼쪽 하위 트리를 방문한 후 루트를 방문할 차례이며, 왼쪽 하위 트리의 루트 노드는 루트의 왼쪽 자식입니다. 이 조건이 충족되는 한, 다음 방문 장소를 찾아보겠습니다.

1. 오른쪽이 비어 있지 않으면 다음 오른쪽 하위 트리의 가장 왼쪽 노드를 찾습니다
2. 오른쪽이 비어 있으면 조상을 찾습니다(자식은 아버지의 왼쪽에 있는 조상입니다).

template<class T>
class _RBTreeIterator
{
    
    
public:
	typedef RBTreeNode<T> Node;
	Node* _node;
	typedef _RBTreeIterator<T> Self;

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

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

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

	Self& operator++()
	{
    
    
		//1.右不为空,找下一个右子树最左节点
		if (_node->_right)
		{
    
    
			Node* min = _node->_right;
			while (min->_left)
			{
    
    
				min = min->_left;
			}
			_node = min;//_node指向它
		}
		else//2.右为空,去找祖先(孩子是父亲左的那个祖先)
		{
    
    
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)//走到最后parent为空
			{
    
    
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}

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

시작과 끝을 다시 구현해 보겠습니다.
반복자는 순서대로이고 첫 번째 유효한 데이터 위치는 5이고 마지막 유효한 위치 옆의 위치는 nullptr입니다.
여기에 이미지 설명을 삽입하세요.

template<class K, class T,class KeyofT>
class RBTree
{
    
    
	typedef RBTreeNode<T> Node;
public:
	typedef _RBTreeIterator<T> iterator;

public:

	iterator begin()
	{
    
    
		Node* left = _root->_left;
		while (left->_left)
		{
    
    
			left = left->_left;
		}
		return iterator(left);
	}
	
	iterator end()
	{
    
    
		return iterator(nullptr);
	}
/
}

다음으로 매핑 및 설정에 반복자를 추가합니다.

//map.h
	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<K, pair<const K, V>, MapKeyofT>::iterator iterator;
		
		bool Insert(const pair<const K, V>& kv)
		{
    
    
			return _t.Insert(kv);
		}

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


RBTree<K, pair<const K, V>, MapKeyofT>::iterator가 정적 변수인지 아니면 여기에 정의된 유형인지 명확하지 않기 때문에 무엇에 대해 typename을 추가해야 하는지에 대해 몇 가지 질문이 있을 수 있습니다 . 따라서 앞에 typename을 추가하면 컴파일러에게 이것이 유형이고 인스턴스화 후에 검색될 수 있음을 알립니다.

인스턴스화되지 않은 클래스 템플릿의 경우 컴파일러는 RBTree<K, pair<const K, V>, MapKeyofT>::iterator의 반복자가 정적 변수인지 유형인지 구분할 수 없습니다. 이 방법으로 유형을 사용할 수도 있습니다. 사용하고 유형 이름을 추가하여 이 클래스 템플릿이 인스턴스화되지 않았지만 이는 유형임을 컴파일러에 알립니다. 먼저 전달한 다음 나중에 가져옵니다. 클래스 템플릿이 인스턴스화됩니다.

//map.h
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<K, pair<const K, V>, MapKeyofT>::iterator iterator;

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

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

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


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


//set.h
template<class K>
	class set
	{
    
    
		struct SetKeyofT
		{
    
    
			const K& operator()(const K& key)
			{
    
    
				return key;
			}
		};
	public:
		typedef typename RBTree<K, K, SetKeyofT>::iterator iterator;

		bool Insert(const K& key)
		{
    
    
			return _t.Insert(key);
		}

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

		iterator end()
		{
    
    
			return _t.end();
		}
	private:
		RBTree<K, K,SetKeyofT> _t;
	};

여기에 이미지 설명을 삽입하세요.
현재는 iterator에 문제가 없는데 정말 문제가 없을까요?
set iterator를 살펴보십시오.
여기에 이미지 설명을 삽입하세요.
일반 iterator는 물론 값을 수정할 수 있지만 Key 값은 set 및 map에서 마음대로 수정할 수 있습니까? 임의 수정 후 기본 레드-블랙 트리가 보장될 수 있습니까?

먼저 set iterator가 라이브러리에서 어떻게 구현되는지 살펴보겠습니다.
여기에 이미지 설명을 삽입하세요.
라이브러리의 set 일반 반복자와 const 반복자는 빨간색-검정 트리 const 반복자를 사용합니다.
그래서 우리는 레드-블랙 트리의 반복자를 완성합니다.

template<class T,class Ref,class Ptr>
class _RBTreeIterator
{
    
    
public:
	typedef RBTreeNode<T> Node;
	Node* _node;
	typedef _RBTreeIterator<T,Ref,Ptr> Self;

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

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

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

	Self& operator++()
	{
    
    
		//1.右不为空,找下一个右子树最左节点
		if (_node->_right)
		{
    
    
			Node* min = _node->_right;
			while (min->_left)
			{
    
    
				min = min->_left;
			}
			_node = min;//_node指向它
		}
		else//2.右为空,去找祖先(孩子是父亲左的那个祖先)
		{
    
    
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)//走到最后parent为空
			{
    
    
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	bool operator!=(const Self& s)
	{
    
    
		return _node != s._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;


public:

	iterator begin()
	{
    
    
		Node* left = _root->_left;
		while (left->_left)
		{
    
    
			left = left->_left;
		}
		return iterator(left);
	}
	
	iterator end()
	{
    
    
		return iterator(nullptr);
	}

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

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

그런 다음 세트의 반복자를 변경하십시오.

	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;

		
		bool Insert(const K& key)
		{
    
    
			return _t.Insert(key);
		}
		
		//那这里该如何调用红黑树的const_iterator
		//因为_t.begin();调用的红黑树的iterator
		iterator begin()
		{
    
    
			return _t.begin();
		}

		iterator end()
		{
    
    
			return _t.end();
		}
	private:
		RBTree<K, K,SetKeyofT> _t;
	};

우리는 여전히 라이브러리를 참조하고 그것이 const를 추가한다는 것을 발견합니다. 이것이 작동하는 이유는 무엇입니까?
여기에 이미지 설명을 삽입하세요.
권한이 좁아질 수 있기 때문입니다. 따라서 일반 객체와 const 객체를 모두 호출할 수 있습니다.
따라서 set의 순방향 반복자(normal 또는 const)는 두 가지 함수를 사용할 수 있으므로 const 반복자에 대해 별도의 함수를 다시 작성할 필요가 없습니다.

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;


		bool Insert(const K& key)
		{
    
    
			return _t.Insert(key);
		}

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

		iterator end() const
		{
    
    
			return _t.end();
		}
	private:
		RBTree<K, K,SetKeyofT> _t;
	};

이러한 방식으로 세트가 일반 반복자인지 상수 반복자인지에 관계없이 Key 값을 수정할 수 없습니다.

지도는 어떻습니까?이런 식으로 반복자를 작성해야 합니까? 사실, 아니요. 맵 반복자는 여전히 이전과 같이 작성됩니다.

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<K, pair<const K, V>, MapKeyofT>::iterator iterator;
		typedef typename RBTree<K, pair<const K, V>, MapKeyofT>::const_iterator const_iterator;

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

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

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

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

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

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

그러면 map은 Key 수정에 대해 걱정하지 않습니다.위의 pair<const K, V>를 참조하세요.첫번째 매개변수로 const를 주었기 때문에 전혀 수정할 수 없습니다. 그리고 V만 수정할 수 있습니다. 이는 우리의 생각과도 일치한다.

여기서는 먼저 반복자(iterator)에 대해 논의하고, 맨 아래에는 역방향 반복자(reverse iterator)도 구현할 예정입니다.

그러면 삽입을 완벽하게 해보자.
여기에 이미지 설명을 삽입하세요.
라이브러리에 있는 insert의 반환 유형은 pair이지만 우리가 직접 작성한 것은 bool뿐입니다.

반환 유형을 삽입한다는 의미입니다. 삽입이 성공하면 반복자는 가장 최근에 삽입된 노드를 가리키고 bool은 true로 설정됩니다. 삽입에 실패하면 동일한 키가 삽입되고 반복자는 삽입된 노드를 가리킵니다. bool은 false로 설정됩니다.

//RBTree.h
pair<iterator,bool> Insert(const T& data)
	{
    
    
		if (_root == nullptr)
		{
    
    
			_root = new Node(data);
			_root->_col = BLACK;
			return make_pair(iterator(_root),true);
		}

		KeyofT kot;
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
    
    
			//比较都要修改
			if (kot(cur->_data) < kot(data))
			{
    
    
				parent = cur;
				cur = cur->_right;
			}
			else if (kot(cur->_data) > kot(data))
			{
    
    
				parent = cur;
				cur = cur->_left;
			}
			else
			{
    
    
				return make_pair(iterator(cur),false);
			}
		}

		//这里要重写申请一个指针,记录当前插入节点
		//否则下面调整情况uncle是红色节点,cur会变
		cur = new Node(data);
		Node* newnode = cur;
		cur->_col = RED;
		if (kot(cur->_data) < kot(parent->_data))
		{
    
    
			parent->_left = cur;
			cur->_parent = parent;
		}
		else
		{
    
    
			parent->_right = cur;
			cur->_parent = parent;
		}

		//调整
		while (parent && parent->_col == RED)
		{
    
    
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
    
    
				Node* uncle = grandfather->_right;
				if (uncle && uncle->_col == RED)//情况一
				{
    
    
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					cur = grandfather;
					//如果是整树,就可能发生野指针,
					parent = cur->_parent;
				}
				else
				{
    
    
					if (cur == parent->_left)//情况二
					{
    
    
						RotaleR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else//情况三
					{
    
    
						RotaleL(parent);
						RotaleR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
			else
			{
    
    
				Node* uncle = grandfather->_left;
				if (uncle && uncle->_col == RED)
				{
    
    
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
    
    
					if (cur == parent->_right)
					{
    
    
						RotaleL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
    
    
						RotaleR(parent);
						RotaleL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}

		}
		_root->_col = BLACK;
		return make_pair(iterator(newnode),true);
	}

또한 지도를 변경하고 설정합니다.

//map.h
		typedef typename RBTree<K, pair<const K, V>, MapKeyofT>::iterator iterator;
		typedef typename RBTree<K, pair<const K, V>, MapKeyofT>::const_iterator const_iterator;

		pair<iterator,bool> Insert(const pair<const K, V>& kv)
		{
    
    
			return _t.Insert(kv);
		}
//set.h
		typedef typename RBTree<K, K, SetKeyofT>::const_iterator iterator;

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

그러나 변경이 이루어진 후 문제가 발생했습니다.
여기에 이미지 설명을 삽입하세요.
set 삽입에 문제가 있어 반환 유형이 일치하지 않습니다. 그리고 지도는 문제가 되지 않습니다.
이는 set의 반복자가 레드-블랙 트리의 const_iterator를 사용하고 기본 레드-블랙 트리 삽입을 호출하는 _t.Insert(key)도 반복자를 사용하여 유형 불일치 문제를 일으키기 때문입니다.
맵 삽입은 문제가 없는데, 레드-블랙 트리의 이터레이터를 사용하는 방식이고, 그렇네요.

이 문제는 맵과 세트에서 발생합니다. 하단에 동일한 블랙맹그로브 템플릿을 사용하기 때문인데, 이런 문제가 발생하면 도서관도 이런 문제에 직면해야 합니다.

참조 라이브러리는 이 문제를 어떻게 해결합니까
여기에 이미지 설명을 삽입하세요.
? 라이브러리는 방금 반복자 문제가 발생했을 때처럼 끝에 const를 추가하지 않습니다.
대신 동일한 유형의 반복자를 사용하여 insert에서 반환된 값을 받은 다음 반환할 때 익명으로 쌍을 구성하세요. 이 쌍에 사용되는 것은 const_iterator입니다. 이는 반환 시 유형 불일치 문제를 해결합니다.

그렇다면 어떻게 반복자를 const_iterator로 구성합니까
?라이브러리가 어떻게 하는지 살펴보겠습니다.
여기에 이미지 설명을 삽입하세요.
언뜻 보면 이것은 복사 구조입니다. 진짜야? 또한 복사 구성을 작성하고 살펴보겠습니다.
여기에 이미지 설명을 삽입하세요.
하지만 여전히 문제가 있습니다. 그럼 정확히 무엇입니까? 일반 반복자를 const 반복자로 변환하는 방법.

좋아요, 관여하지 마세요. 핵심은 typedef에서 반복자를 만드는 것입니다.
여기에 이미지 설명을 삽입하세요.
우리가 전달하는 첫 번째 매개 변수는 T에서 제공하는 값입니다. 이는 <T, T&, T*>와 동일합니다.
다음 함수의 경우 동일한 유형을 사용하십시오. 반복자는 복사 구성됩니다. 다양한 유형의 반복자를 사용하는 경우 이는 생성자를 기준으로 합니다.

여기에 이미지 설명을 삽입하세요.
이는 생성자를 호출하여 반복자를 const_iterator로 구성하는 것입니다.
세트 삽입을 수정합니다. 반환시 유형 불일치 문제를 해결하려면 지금 함수를 채우십시오.

//set.h
		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);
		}

다음으로 map에 [ ] 인터페이스를 구현합니다.

Map의 [ ] 인터페이스 시뮬레이션 구현

지도의 [ ] 인터페이스를 이용하면 매우 편리하며 삽입, 수정, 검색(존재하는 경우에만 확인 가능, 존재하지 않는 경우 삽입)이 가능하다. 그런 다음 구현하십시오. 이번 부분 은 [C++] map과 set의 사용법과 map 인터페이스 사용 시 주의사항에 대해 자세히 설명합니다 . 여기서 구현하면 됩니다.

V& operator[](const K& key)
{
    
    
	//插入key,返回V,然后就可以修改V了
	pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));
	return ret.first->second;
}

정방향 반복기의 전체 코드 + 역방향 반복기의 시뮬레이션 구현

역방향 반복자는 컨테이너 어댑터이며, 순방향 반복자 때문에 역방향 반복자를 조정할 수 있습니다.

자세한 내용은 여기를 참조하세요. [C++] Priority_queue 시뮬레이션 구현 + 펑터 + 역방향 반복자

정방향 반복자를 호출하려면 역방향 반복자에서 연산자를 다시 로드하면 됩니다.

template<class iterator, class Ref, class Ptr>
class _RBTreeReverseIterator
{
    
    
public:
	typedef _RBTreeReverseIterator<iterator, Ref, Ptr> Self;
public:
	_RBTreeReverseIterator(iterator it)
		:_it(it)
	{
    
    }


private:
	iterator _it;
};

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;
	//反向迭代器
	typedef _RBTreeReverseIterator<iterator, T&, T*> reverse_iterator;
	typedef _RBTreeReverseIterator<const_iterator, const T&, const T*> const_reverse_iterator;
	///
}

먼저 정방향 반복자에서 누락된 몇 가지 사항을 보완해 보겠습니다.

추가해야 할 가장 중요한 점은
실제로 ++ 의 정반대라는 점 입니다
. 15의 위치에 있으며 이는 루트가 15인 이 하위 트리의 경우 루트 및 오른쪽 하위 트리를 방문했음을 의미합니다. 이제 왼쪽 하위 트리를 방문할 시간입니다. 왼쪽 하위 트리는 두 가지 상황으로 나누어집니다. 왼쪽 하위 트리가 존재하거나 존재하지 않습니다.
여기에 이미지 설명을 삽입하세요.
1. 왼쪽은 비어 있지 않으며, 왼쪽 하위 트리의 다음 가장 오른쪽 노드
2. 왼쪽은 비어 있습니다. 찾기 조상(자녀는 아버지의 오른쪽에 있는 조상임)

순방향 반복자의 전체 코드

template<class T,class Ref,class Ptr>
class _RBTreeIterator
{
    
    
public:
	typedef RBTreeNode<T> Node;
	Node* _node;
	typedef _RBTreeIterator<T,Ref,Ptr> Self;
	typedef _RBTreeIterator<T, T&, T*> iterator;


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

	_RBTreeIterator(const iterator& s)
		:_node(s._node)
	{
    
    }

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

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

	//++it
	Self& operator++()
	{
    
    
		//1.右不为空,找下一个右子树最左节点
		if (_node->_right)
		{
    
    
			Node* min = _node->_right;
			while (min->_left)
			{
    
    
				min = min->_left;
			}
			_node = min;//_node指向它
		}
		else//2.右为空,去找祖先(孩子是父亲左的那个祖先)
		{
    
    
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)//走到最后parent为空
			{
    
    
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	//it++
	Self operator(int)
	{
    
    
		Self tmp(*this);
		operator++();
		return tmp;
	}

	//--it
	Self& operator--()//1.左不为空,下一个左子树最右节点
	{
    
    
		if (_node->_left)
		{
    
    
			Node* max = _node->_left;
			while (max->_right)
			{
    
    
				max = max->_right;
			}
			_node = max;
		}
		else//2.左为空,去找祖先(孩子是父亲右的那个祖先)
		{
    
    
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
    
    
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	//it--
	Self operator--(int)
	{
    
    
		Self tmp(*this);
		operator--();
		return tmp;
	}

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

역반복자 작성을 시작해 보겠습니다. 실제로는 매우 간단하고 직접 재사용할 수 있습니다.

++, rit는 처음에 위치 15를 가리키며, ++는 정방향 반복자와 동일합니다.
여기에 이미지 설명을 삽입하세요.
역방향 반복자의 전체 코드입니다.

template<class iterator, class Ref, class Ptr>
class _RBTreeReverseIterator
{
    
    
public:
	typedef _RBTreeReverseIterator<iterator, Ref, Ptr> Self;
public:
	_RBTreeReverseIterator(iterator it)
		:_it(it)
	{
    
    }

	Ref operator*()
	{
    
    
		return *_it;
	}

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

	//++it
	Self& operator++()
	{
    
    
		--_it;
		return *this;
	}

	//it++
	Self operator++(int)
	{
    
    
		Self tmp(*this);
		++_it;
		return tmp;
	}

	//--it
	Self& operator--()
	{
    
    
		++_it;
		return *this;
	}

	//it--
	Self operator--(int)
	{
    
    
		Self tmp(*this);
		++_it;
		return tmp;
	}

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

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


private:
	iterator _it;
};

레드-블랙 트리, 맵을 추가하고 역방향 반복자를 설정해 보겠습니다.

//RBTree.h
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;
	//反向迭代器
	typedef _RBTreeReverseIterator<iterator, T&, T*> reverse_iterator;
	typedef _RBTreeReverseIterator<const_iterator, const T&, const T*> const_reverse_iterator;


public:

	iterator begin()
	{
    
    
		Node* left = _root->_left;
		while (left->_left)
		{
    
    
			left = left->_left;
		}
		return iterator(left);
	}
	
	iterator end()
	{
    
    
		return iterator(nullptr);
	}

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

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

	reverse_iterator rbegin()
	{
    
    
		Node* right = _root;
		while (right && right->_right)
		{
    
    
			right = right->_right;
		}
		return reverse_iterator(right);
	}

	reverse_iterator rend()
	{
    
    
		return reverse_iterator(nullptr);
	}

	const_reverse_iterator rbegin() const
	{
    
    
		Node* right = _root;
		while (right && right->_right)
		{
    
    
			right = right->_right;
		}
		return const_reverse_iterator(right);
	}

	const_reverse_iterator rend() const
	{
    
    
		return const_reverse_iterator(nullptr);
	}
	/
}
//map.h
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<K, pair<const K, V>, MapKeyofT>::iterator iterator;
		typedef typename RBTree<K, pair<const K, V>, MapKeyofT>::const_iterator const_iterator;
		typedef typename RBTree<K, pair<const K, V>, MapKeyofT>::reverse_iterator reverse_iterator;
		typedef typename RBTree<K, pair<const K, V>, MapKeyofT>::const_reverse_iterator const_reverse_iterator;
		

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

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

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

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

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

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

		reverse_iterator rbegin()
		{
    
    
			return _t.rbegin();
		}

		reverse_iterator rend()
		{
    
    
			return _t.rend();
		}

		const_reverse_iterator rbegin() const
		{
    
    
			return _t.rbegin();
		}

		const_reverse_iterator rend() const
		{
    
    
			return _t.rend();
		}

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

//set.h
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;
		typedef typename RBTree<K, K, SetKeyofT>::const_reverse_iterator reverse_iterator;
		typedef typename RBTree<K, K, SetKeyofT>::const_reverse_iterator const_reverse_iterator;


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

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

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

		reverse_iterator rend() const
		{
    
    
			return _t.rend();
		}

	private:
		RBTree<K, K,SetKeyofT> _t;
	};

지도와 세트의 시뮬레이션 구현은 여기까지입니다. 아직 구현되지 않은 인터페이스도 있으므로 관심 있는 친구들은 직접 구현해 볼 수 있습니다.

추천

출처blog.csdn.net/fight_p/article/details/135034124