C++map 의 간단 한 사용 실현

17949 단어 C++map
맵 과 set 의 밑바닥 은 모두 붉 은 검 은 나 무 를 통 해 이 루어 지지 만 원생 태 의 붉 은 검 은 나무 가 아니 라 개 조 된 붉 은 검 은 나무 이다.그리고 용 기 는 각자 의 클래스 에 독특한 함 수 를 추가 하여 각자 의 적합 한 문 제 를 해결한다
맵 과 set 밑바닥 은 개 조 된 붉 은 검 은 나무 입 니 다.개 조 된 검 은 나 무 를 먼저 살 펴 보 겠 습 니 다.
在这里插入图片描述
일반적인 붉 은 검 은 나무 와 달리 뿌리 노드 에 머리 결점 을 하나 더 했다.이 결점 은 진실 한 결점 이 아니 라 보조 결점 일 뿐 뒤에 붉 은 검 은 나무의 교체 기 를 실현 하기 위해 나타 난 것 이다.이 header 결점 의 부모 노드 는 바로 진실 한 뿌리 노드 이 고 왼쪽 아 이 는 이 나무의 가장 왼쪽 결점 이 며 오른쪽 아 이 는 이 나무의 가장 오른쪽 노드 이다.
우 리 는 지금 STL 소스 코드 를 통 해 map 와 set 에서 어떻게 붉 은 검 은 나 무 를 이용 하여 각자 의 서로 다른 기능 을 실현 하 는 지 간단하게 분석 하고 있다.
在这里插入图片描述
맵 에 두 개의 일반적인 매개 변수 가 있 는데 하 나 는 Key 이 고 하 나 는 T,즉 value 입 니 다.그 중 에 키 의 별명 은 키 입 니 다.type,그리고 Key 와 T 를 pair 대상 의 일반적인 매개 변수 로 하고 별명 을 value 로 바 꿉 니 다.type。멤버 는 나무 가 한 그루 밖 에 없 는데 이 나 무 는 빨간색 과 검은색 나무 이 고 빨간색 과 검은색 나 무 는 두 개의 일반적인 매개 변수 가 있 으 며 하 나 는 Key 이 고 하 나 는 Value 이다.Key 는 레 드 블랙 트 리 의 결점 값 의 유형 이 고 Value 는 결점 Key 에 대응 하 는 Value 값 이다.결점 에서 하나의 결점 류 를 계승 하 였 는데,그것 은 상당히 5 명의 구성원 이 있 는데,색깔,부류 지침,왼쪽 아이 지침,오른쪽 아이 지침,결점 의 값 이다.
set 에 일반적인 인자 Key 만 있 습 니 다.이 용기 에 서 는 빨 간 검 은 나무 밑 에 두 개의 일반적인 인 자 를 제공 해 야 하기 때문에 set 는 vkey 를 value 로 합 니 다.이때 전 달 된 빨 간 검 은 나무의 일반적인 매개 변 수 는 모두 Key 이다.
그래서 두 용기 가 다 르 기 때문에 가장 관건 적 인 것 은 value 유형 이 다 르 고 map 용기 밑 에 있 는 빨 간 검 은 나무의 value 는 pair 대상 입 니 다.set 용기 에 있 는 빨 간 검 은 나무의 value 는 set 자체 의 key 입 니 다.붉 은 검 은 나무 에서 특수 처 리 를 하면 그들의 value 를 얻 을 수 있다.
在这里插入图片描述
다음 코드 는 붉 은 검 은 나무 에 대한 개선 으로 맵 과 set 관련 용기 에 적합 합 니 다.

#include <iostream>
#include <utility>
#include <algorithm>
using namespace std;

enum COLOR
{
	BLACK,
	RED
};

template <class V>
struct RBTreeNode
{
	RBTreeNode<V>* _parent; //   
	RBTreeNode<V>* _left; //   
	RBTreeNode<V>* _right; //   
	V _val;
	COLOR _color; //  

	RBTreeNode(const V& val = V())
		:_parent(nullptr)
		, _left(nullptr)
		, _right(nullptr)
		, _val(val)
		, _color(RED)
	{}
};

template <class K, class V, class KeyOfValue>
class RBTree
{
public:
	typedef RBTreeNode<V> Node;

	RBTree()
		:_header(new Node)
	{
		//    
		_header->_left = _header->_right = _header;
	}

	bool insert(const V& val)
	{
		if (_header->_parent == nullptr)
		{
			Node* root = new Node(val);

			_header->_parent = root;
			root->_parent = _header;
			_header->_left = _header->_right = root;

			//      
			root->_color = BLACK;
			return true;
		}

		Node* cur = _header->_parent;
		Node* parent = nullptr;

		KeyOfValue kov;
		//1.            
		while (cur)
		{
			parent = cur;
			if (kov(cur->_val) == kov(val))
				return false;
			else if (kov(cur->_val) > kov(val))
				cur = cur->_left;
			else
				cur = cur->_right;
		}
		//2.    
		cur = new Node(val);
		if (kov(parent->_val) > kov(cur->_val))
			parent->_left = cur;
		else
			parent->_right = cur;
		cur->_parent = parent;

		//3.            
		while (cur != _header->_parent && cur->_parent->_color == RED)//          ,     
		{
			parent = cur->_parent;
			Node* gfather = parent->_parent;

			if (gfather->_left == parent)
			{
				Node* uncle = gfather->_right;
				//  1.uncle     
				if (uncle && uncle->_color == RED)
				{
					parent->_color = uncle->_color = BLACK;
					gfather->_color = RED;
					//    
					cur = gfather;
				}
				else
				{
					if (parent->_right == cur)//  3
					{
						RotateL(parent);
						swap(cur, parent);
					}
					//  2.uncle     uncle  
					RotateR(gfather);
					parent->_color = BLACK;
					gfather->_color = RED;
					break;
				}
			}
			else
			{
				Node* uncle = gfather->_left;
				if (uncle && uncle->_color == RED)
				{
					parent->_color = uncle->_color = BLACK;
					gfather->_color = RED;
					//    
					cur = gfather;
				}
				else
				{
					if (parent->_left == cur)
					{
						RotateR(parent);
						swap(cur, parent);
					}

					RotateL(gfather);
					parent->_color = BLACK;
					gfather->_color = RED;
					break;
				}
			}
		}

		//      
		_header->_parent->_color = BLACK;
		//          
		_header->_left = leftMost();
		_header->_right = rightMost();
		return true;
	}

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

		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;
		if (parent == _header->_parent)
		{
			_header->_parent = subR;
			parent->_parent = _header;
		}
		else
		{
			Node* gfather = parent->_parent;
			if (gfather->_left == parent)
				gfather->_left = subR;
			else
				gfather->_right = subR;
			subR->_parent = gfather;
		}
		subR->_left = parent;
		parent->_parent = subR;
	}

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

		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;

		if (parent == _header->_parent)
		{
			_header->_parent = subL;
			subL->_parent = _header;
		}
		else
		{
			Node* gfather = parent->_parent;
			if (gfather->_left == parent)
				gfather->_left = subL;
			else
				gfather->_right = subL;
			subL->_parent = gfather;
		}
		subL->_right = parent;
		parent->_parent = subL;
	}

	Node* leftMost()
	{
		Node* cur = _header->_parent;
		while (cur && cur->_left)
		{
			cur = cur->_left;
		}
		return cur;
	}

	Node* rightMost()
	{
		Node* cur = _header->_parent;
		while (cur && cur->_right)
		{
			cur = cur->_right;
		}
		return cur;
	}
private:
	Node* _header;
};

template<class K, class T>
class Map
{
	struct MapKeyOfValue
	{
		const K& operator()(const pair<K, T>& val)
		{
			return val.first;
		}
	};
public:
	bool insert(const pair<K, T>& kv)
	{
		return _rbt.insert(kv);
	}

	T& operator[](const K& key)
	{
		bool ret = _rbt.insert(make_pair(key, T()));
	}

private:
	typedef RBTree<K, pair<K, T>, MapKeyOfValue> rbt;
	rbt _rbt;
};

template <class K>
class Set
{
	struct SetKeyOfValue
	{
		const K& operator()(const K& val)
		{
			return val;
		}
	};

public:
	bool insert(const K& val)
	{
		return _rbt.insert(val);
	}

private:
	typedef RBTree<K, K, SetKeyOfValue> rbt;
	rbt _rbt;
};
교체 기
우리 원생 의 Node 결점 교체 기 는 교체 기의 일반적인 조작 을 실현 할 수 없 기 때문에 우 리 는 결점 에 대해 다른 층 의 포장 을 하고 해당 하 는 조작 연산 자 를 다시 불 러 와 야 한다.이 클래스 에서 구성원 변 수 는 Node 노드 입 니 다.
교체 기 에 대한 인용 은 교체 기의 val 값 을 얻 는 것 입 니 다.

	V& operator*()
	{
		return _node->_val;
	}
교체 기 화살 표를 조작 하 는 것 은 교체 기 값 을 얻 는 주소 입 니 다.

V* operator->()
	{
		return &_node->_val;
	}
두 개의 교체 기의 판 등 조작 은 결점 의 주소 가 같 습 니까?

	bool operator!=(const Self& it)
	{
		return _node != it._node;
	}
교체 기의+와-중간 순서 에 맞 게 질서 있 게 옮 겨 다 니 는 것 입 니 다.다음은 분석 하 겠 습 니 다.
교체 기의 begin()위 치 는 나무 에서 가장 작은 결점 이 어야 하 며,나무 에서 가장 작은 결점 은 나무의 가장 왼쪽 결점 이다.교체 기의 end()위 치 는 나무 에서 가장 큰 결점 이 어야 하 며,나무 에서 가장 큰 결점 은 나무의 가장 오른쪽 결점 이다.
在这里插入图片描述
교체 기의+작업 은 두 가지 상황 으로 나 뉘 는데 첫 번 째 는 오른쪽 나무 가 존재 하 는 상황 이 고 두 번 째 는 오른쪽 나무 가 존재 하지 않 는 상황 이다.
오른쪽 트 리 가 존재 할 때,우 리 는 오른쪽 트 리 의 가장 왼쪽 노드 를 옮 겨 다 니 며 이 노드 를 방문 해 야 한다.예 를 들 어 현재 우리 의 교체 기 는 8 의 위치 에 있 습 니 다.중간 순서 로 옮 겨 다 니 는 조건 에 따라 우 리 는 10 번 노드 를 방문 해 야 합 니 다.그래서 우 리 는 먼저 8 점 의 오른쪽 서브 트 리 11 번 노드 에 도착 한 다음 에 11 의 가장 왼쪽 노드 를 옮 겨 다 녀 야 합 니 다.이 노드 는 10 이 고 바로 우리 가 방문 해 야 할 노드 입 니 다.
在这里插入图片描述
在这里插入图片描述

		if (_node->_right)
		{
			//        
			_node = _node->_right;
			while (_node->_left)
			{
				_node = _node->_left;
			}
		}
두 번 째 상황 은 오른쪽 나무 가 존재 하지 않 는 다 는 것 이다.오른쪽 나무 가 존재 하지 않 을 때 우 리 는 위로 거 슬러 올 라 가 야 한다.중간 순서 가 옮 겨 다 니 는 규칙 은 바로 왼쪽 아이,뿌리,오른쪽 아이 이다.그래서 우 리 는 현재 노드 가 부모 노드 의 왼쪽 아이 인지 오른쪽 아이 인지 판단 해 야 한다.만약 에 왼쪽 아이 라면 부모 노드 가 아직 방문 하지 않 았 음 을 나타 내 고 이때 부모 노드 를 방문 해 야 한다.오른쪽 아이 라면 부모 노드 도 방 문 했 음 을 나타 내 고 위로 거 슬러 올 라 가 이 노드 가 부모 노드 가 아 닌 오른쪽 아이 가 될 때 까지 거 슬러 올 라 가 야 한다.예 를 들 어 우리 노드 는 5 번 노드 의 위치 에 있 기 때문에 이 노드 의 부모 노드 6 번 노드 의 왼쪽 인지 오른쪽 인지 판단 해 야 한다.이때 5 번 노드 와 6 번 노드 의 왼쪽 은 6 번 노드 도 방문 하지 않 았 음 을 나 타 낼 수 있다.이때 교체 기 가 업 데 이 트 된 부모 노드 의 위 치 를 표시 하면 된다.
在这里插入图片描述
우리 교체 기 가 7 번 노드 에 있 을 때 7 번 노드 는 6 번 노드 의 오른쪽 에 있 습 니 다.이 때 는 위로 거 슬러 올 라 가 야 합 니 다.노드 가 부모 노드 를 업데이트 시 킨 다음 에 아버지 노드 의 위 치 를 위로 거 슬러 올 라 가서 현재 노드 의 위치 가 아버지 노드 의 오른쪽 인지 판단 해 야 합 니 다.이때 6 번 노드 는 8 번 노드 의 왼쪽 이 고 8 번 노드 가 아직 방문 하지 않 았 음 을 나타 냅 니 다.이때 8 번 노드 를 방문 하면 됩 니 다.
在这里插入图片描述
그러나 우 리 는 이 나무의 뿌리 부분 에 오른쪽 아이 가 없 을 때 특별한 상황 을 고려 해 야 한다.
在这里插入图片描述
정상적으로 말하자면,우 리 는 교체 기의++조작 에 대해 빈 머리 결점 의 위치 로 이동 할 수 있 지만,우리 가 다시 거 슬러 올 라 가 는 과정 에서 문제 가 발생 할 수 있다.이때 it 에 오른쪽 결점 이 없 기 때문에 이 결점 이 부모 노드 의 왼쪽 인지 오른쪽 인지 판단 해 야 합 니 다.이때 오른쪽 이면 위로 거 슬러 올 라 갑 니 다.
在这里插入图片描述
이러한 교체 기 에 대한++는 죽은 조작 으로 영원히 빈 위치 에 가지 않 을 것 이다.그래서 우 리 는 결점 과 특별한 처 리 를 해 야 한다.node 노드 의 오른쪽 아이 가 자신의 아버지 일 때 노드 를 업데이트 하지 않 아 도 됩 니 다.이 때 는 end()에 도 착 했 습 니 다.parent 노드 를 업데이트 하면 이+작업 은 변 하지 않 습 니 다.
저희 가 지금 테스트 를 진행 하고 있 습 니 다.
교체 기 부분 코드

template <class  V>
struct RBTreeIterator
{
	typedef RBTreeNode<V> Node;
	typedef RBTreeIterator<V> Self;
	Node* _node;

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

	//   
	V& operator*()
	{
		return _node->_val;
	}

	V* operator->()
	{
		return &_node->_val;
	}

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

	Self& operator++()
	{
		if (_node->_right) //     
		{
			//        
			_node = _node->_right;
			while (_node->_left)
			{
				_node = _node->_left;
			}
		}
		else //      
		{
			Node* parent = _node->_parent;
			while (_node == parent->_right)//  
			{
				_node = parent;
				parent = parent->_parent;
			}
			//    :        ,        
			if (_node->_right != parent) 
				_node = parent;
		}
		return *this;
	}
};
레 드 블랙 트 리 교체 기 코드 추가

	typedef RBTreeIterator<V> iterator;

	iterator begin()
	{
		return iterator(_header->_left);
	}
	iterator end()
	{
		return iterator(_header);
	}
map 에 레 드 블랙 트 리 교체 기 코드 추가

	typedef typename RBTree<K, pair<K, T>, MapKeyOfValue>::iterator iterator;

	iterator begin()
	{
		return _rbt.begin();
	}
	iterator end()
	{
		return _rbt.end();
	}
테스트 결과:
在这里插入图片描述
여기 서 교체 기--코드 를 직접 드 립 니 다.원리 와+유사 합 니 다.
교체 기 클래스 에서

	Self& operator--()
	{
		if (_node->_left)
		{
			//        
			_node = _node->_left;
			while (_node->_right)
			{
				_node = _node->_right;
			}
		}
		else
		{
			Node* parent = _node->_parent;
			while (_node == parent->_left)
			{
				_node = parent;
				parent = parent->_parent;
			}
			if (_node->_left != parent)
				_node = parent;
		}
		return *this;
	}
붉 은 검 은 나무 류 에 역방향 교체 기 를 추가 하여 테스트 에 사용 합 니 다.

iterator rbegin()
	{
		return iterator(_header->_right);
	}
맵 에 도 역방향 교체 기 추가

	iterator rbegin()
	{
		return _rbt.rbegin();
	}
테스트:
在这里插入图片描述
네모 난 괄호[]
삽 입 된 반환 값 은 pair 대상 을 되 돌려 주 는 것 입 니 다.따라서 삽입 할 때 되 돌아 오 는 값 은 pair 대상 으로 수정 되 어야 합 니 다.pair 대상 의 first 는 삽 입 된 노드 의 교체 기 이 고,second 는 삽입 성공 여부 입 니 다.

	pair<iterator, bool> insert(const V& val)
	{
		if (_header->_parent == nullptr)
		{
			Node* root = new Node(val);

			_header->_parent = root;
			root->_parent = _header;
			_header->_left = _header->_right = root;

			//      
			root->_color = BLACK;
			return make_pair(iterator(root), true);
		}

		Node* cur = _header->_parent;
		Node* parent = nullptr;

		KeyOfValue kov;
		//1.            
		while (cur)
		{
			parent = cur;
			if (kov(cur->_val) == kov(val))
				return make_pair(iterator(cur), false);
			else if (kov(cur->_val) > kov(val))
				cur = cur->_left;
			else
				cur = cur->_right;
		}
		//2.    
		cur = new Node(val);
		Node* node = cur;
		if (kov(parent->_val) > kov(cur->_val))
			parent->_left = cur;
		else
			parent->_right = cur;
		cur->_parent = parent;

		//3.            
		while (cur != _header->_parent && cur->_parent->_color == RED)//          ,     
		{
			parent = cur->_parent;
			Node* gfather = parent->_parent;

			if (gfather->_left == parent)
			{
				Node* uncle = gfather->_right;
				//  1.uncle     
				if (uncle && uncle->_color == RED)
				{
					parent->_color = uncle->_color = BLACK;
					gfather->_color = RED;
					//    
					cur = gfather;
				}
				else
				{
					if (parent->_right == cur)//  3
					{
						RotateL(parent);
						swap(cur, parent);
					}
					//  2.uncle     uncle  
					RotateR(gfather);
					parent->_color = BLACK;
					gfather->_color = RED;
					break;
				}
			}
			else
			{
				Node* uncle = gfather->_left;
				if (uncle && uncle->_color == RED)
				{
					parent->_color = uncle->_color = BLACK;
					gfather->_color = RED;
					//    
					cur = gfather;
				}
				else
				{
					if (parent->_left == cur)
					{
						RotateR(parent);
						swap(cur, parent);
					}

					RotateL(gfather);
					parent->_color = BLACK;
					gfather->_color = RED;
					break;
				}
			}
		}

		//      
		_header->_parent->_color = BLACK;
		//          
		_header->_left = leftMost();
		_header->_right = rightMost();
		//return true;
		return make_pair(iterator(node), true);
	}

맵 에서 도 수정 해 야 돼 요.

	pair<iterator, bool> insert(const pair<K, T>& kv)
	{
		return _rbt.insert(kv);
	}

	T& operator[](const K& key)
	{
		pair<iterator, bool> ret = _rbt.insert(make_pair(key, T()));
		//ret.first    
		//   -> pair<k,v>  
		//pair<k,v>->second,  v
		return ret.first->second; 
	}
테스트:
在这里插入图片描述
C++map 의 간단 한 사용 실현 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 관련 C++map 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 도 많은 응원 부 탁 드 리 겠 습 니 다!

좋은 웹페이지 즐겨찾기