ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 레드 블랙 트리 구현 및 테스트
    프로그래밍 기초/알고리즘 2021. 9. 19. 17:03

      레드 블랙 트리 

    이진 검색 트리를 기반으로 노드에 색상을 추가하여 색상 규칙을 기준으로 트리의 균형을 유지한다.

     

    ※ 레드 블랙 트리는 노드의 수가 n일 때 최대 깊이가 Ο(logn)이 되게 된다.

     

    레드 블랙 트리의 삽입 알고리즘

    레드 블랙 트리의 삽입에서는 먼저 삽입하는 노드의 색상을 레드로 간주하고 삽입을 진행하며 삽입한 노드를 x, 부모 노드를 p, 형제 노드를 s라고 할 때 발생 가능한 모든 경우는 다음과 같다.

    • p가 블랙인 경우 삽입 연산을 종료한다.
    • s가 레드인 경우 p와 s를 블랙으로 바꾸고 p²을 레드로 바꾼다. 만약 p²가 루트면 p²를 블랙으로 바꾸고 종료한다. 연산 결과 p²이 레드이면 p²을 문제 발생 노드로 두고 재귀적으로 처리한다.
    • s가 블랙인 경우 삽입된 위치에 따른 추가 연산을 진행한다.
      • x가 p의 오른쪽 자식인 경우(Case 2.1) p를 중심으로 왼쪽으로 회전한 뒤 Case 2.2를 진행한다.
      • x가 p의 왼쪽 자식인 경우(Case 2.2) p²을 중심으로 오른쪽으로 회전하고 p와 p²의 색을 서로 바꾼다.

     

    쉽게 배우는 알고리즘 레드 블랙 트리 그림 5-11

     

    레드 블랙 트리의 삭제 알고리즘

    레드 블랙 트리의 삭제는 기본적으로 이진 검색 트리의 삭제 방법에 따라 삭제한 뒤 색상 규칙에 맞게 정렬한다.

     

    자식이 두 개인 경우 오른쪽 자식의 최소 노드의 값과 삭제하려는 노드의 값을 교환한 뒤 최소 노드를 삭제하는 연산을 진행하는데 이때 값의 교환은 레드 블랙 트리의 속성을 바꾸지 않으므로 노드의 색상을 다시 정렬하는 것은 자식이 없거나 하나만 존재하는 경우만 진행한다고 생각해도 된다.

     

    삭제하려는 노드를 m, 자식 노드를 x, 부모 노드를 p, 형제 노드를 s라고 할 때 처할 수 있는 상황은 다음과 같다.

    • m이 레드인 경우 삭제 연산을 종료한다.
    • m이 블랙이지만 x가 레드인 경우 삭제 연산을 종료한다.
    • m이 블랙이고 x도 블랙인 경우 주변 상황에 따라 재정렬해 줘야 한다.

     

    쉽게 배우는 알고리즘 레드 블랙 트리 그림 5-12, 5-13

     

    자식 노드를 x, 부모 노드를 p, 형제 노드를 s, 형제의 왼쪽 자식을 l, 형제의 오른쪽 자식을 r이라 할 때 삭제 후 재 정렬해야 하는 모든 경우는 다음과 같다.

    • p가 레드, s는 블랙인 경우 <l의 색상, r의 색상>
      • Case 1-1 <블랙, 블랙>
      • Case 1-2 <레드 또는 블랙(*), 레드>
      • Case 1-3 <레드, 블랙>
    • p가 블랙 <s의 색상, l의 색상, r의 색상>
      • Case 2-1 <블랙, 블랙, 블랙>
      • Case 2-2 <블랙, 레드 또는 블랙(*), 레드>
      • Case 2-3 <블랙, 레드, 블랙>
      • Case 2-4 <레드, 블랙, 블랙>

     

    ※ Case 1-2와 Case 2-2 그리고 Case 1-3과 Case 2-3은 p의 색상이 처리 방법에 영향을 미치지 않으므로 통합한다.

     

    쉽게 배우는 알고리즘 레드 블랙 트리 그림 5-15

     

    각각의 상태를 처리하는 방법은 다음과 같다.

    • Case 1-1 p와 s의 색상을 맞바꾼다.
    • Case *-2 p를 중심으로 왼쪽으로 회전시키고, p와 s의 색상을 바꾼 다음 r의 색상을 레드에서 블랙으로 바꾼다.
    • Case *-3 s를 중심으로 오른쪽으로 회전시키고 l과 s의 색상을 맞바꾼다. 이후 Case *-2를 진행한다.
    • Case 2-1 s의 색상을 블랙에서 레드로 바꾼다. 이때 p에 대해서 문제가 발생하므로 p를 문제 노드로 하여 처리를 진행한다.
    • Case 2-4 p를 중심으로 왼쪽으로 회전시키고 p와 s의 색상을 맞바꾼다. 문제 발생 노드인 x의 부모가 바뀌게 되므로 노드의 색상에 따라 Case 1-x를 진행한다.

     

    쉽게 배우는 알고리즘 레드 블랙 트리 그림 5-16

     

    레드 블랙 트리 구현

    이진 검색 트리를 기반으로 하여 레드 블랙 트리를 구현한다.

     

    RedBlackTree.h

    #pragma once
    #include "../Common.h"
    #include <string>
    #include <queue>
    #include <map>
    
    using std::string;
    using std::queue;
    using std::map;
    
    enum class NodeColor
    {
    	Red,
    	Black
    };
    
    /// <summary>
    /// 레드 블랙 트리에 사용할 노드
    /// </summary>
    struct RedBlackNode
    {
    	const static int NodeSize = 5;
    	const static int ColorSize = 3;
    	// NodeSize + ColorSize
    	const static int Width = 8;
    
    	RedBlackNode() : isEmpty{ false }, data { 0 }, color{ NodeColor::Red },
    		parent{ nullptr }, left{ nullptr }, right{ nullptr } {}
    	RedBlackNode(int data) : isEmpty{ false }, data{ data }, color{ NodeColor::Red }, 
    		parent{ nullptr }, left{ nullptr }, right{ nullptr } {}
    
    	void Clear()
    	{
    		isEmpty = false;
    		data = 0;
    		parent = nullptr;
    		left = nullptr;
    		right = nullptr;
    	}
    
    	RedBlackNode* GetSibling()
    	{
    		RedBlackNode* sibling{ nullptr };
    
    		if (parent != nullptr)
    		{
    			if (parent->left == this)
    			{
    				sibling = parent->right;
    			}
    			else
    			{
    				sibling = parent->left;
    			}
    		}
    		return sibling;
    	}
    
    	void SwapColor(RedBlackNode* other)
    	{
    		NodeColor temp{ other->color };
    		other->color = color;
    		color = temp;
    	}
    
    	int GetMaxDepth()
    	{
    		int leftMaxDepth{ !left->isEmpty ? left->GetMaxDepth() : 0 };
    		int rightMaxDpth{ !right->isEmpty ? right->GetMaxDepth() : 0 };
    		return (leftMaxDepth > rightMaxDpth ? leftMaxDepth : rightMaxDpth) + 1;
    	}
    
    	int GetCurDepth()
    	{
    		return (parent != nullptr ? parent->GetCurDepth() : 0) + 1;
    	}
    
    	bool HasLeftChild()
    	{
    		return left != nullptr;
    	}
    
    	bool HasRightChild()
    	{
    		return right != nullptr;
    	}
    
    	string ToString()
    	{
    		if (isEmpty)
    		{
    			return string(Width, ' ');
    		}
    
    		string dataStr = std::to_string(data);
    		size_t spaceCnt = NodeSize - dataStr.size();
    		size_t leftSpaceCnt = spaceCnt / 2;
    		size_t rightSpaceCnt = spaceCnt / 2 + spaceCnt % 2;
    
    		string colorText = (color == NodeColor::Black ? "B" : "R");
    
    		return string(leftSpaceCnt, '_') + dataStr 
    			+ string(rightSpaceCnt, '_') + "(" + colorText + ")";
    	}
    
    	bool isEmpty;
    
    	int data;
    	NodeColor color;
    	RedBlackNode* parent;
    	RedBlackNode* left;
    	RedBlackNode* right;
    };
    
    /// <summary>
    /// 레드 블랙 트리에서 노드의 재활용을 위한 매니저
    /// </summary>
    class RedBlackNodeManager
    {
    public:
    	RedBlackNodeManager() : _nil(nullptr), _nodes(nullptr) {}
    	~RedBlackNodeManager();
    
    	void RegisterNilNode(RedBlackNode* nil);
    
    	void Push(RedBlackNode* node);
    	RedBlackNode* Pop();
    	RedBlackNode* GetEmptyNode(RedBlackNode* parent);
    
    private:
    	RedBlackNode* _nil;
    	RedBlackNode* _nodes;
    };
    
    /// <summary>
    /// 연결 자료구조를 이용한 레드 블랙 트리
    /// </summary>
    class RedBlackTree
    {
    public:
    	RedBlackTree();
    	~RedBlackTree();
    
    	bool Exists(int data);
    	const RedBlackNode& Search(int data);
    	void Insert(int data);
    	void Delete(int data);
    
    	void PrintTree();
    
    private:
    	RedBlackNode* Insert(RedBlackNode* parent, int data);
    	void AdjustInsertedNode(RedBlackNode* node);
    
    	RedBlackNode* Delete(RedBlackNode* node);
    	void AdjustDeletedNode(RedBlackNode* node);
    
    	void PrintTree(RedBlackNode* node, int lineWidth);
    	string GetNodeStick(RedBlackNode* node, int blankSize);
    
    	RedBlackNode* GetNode(int data);
    
    	bool IsLeftNode(RedBlackNode* node);
    	bool IsRightNode(RedBlackNode* node);
    
    	void RotateLeft(RedBlackNode* node);
    	void RotateRight(RedBlackNode* node);
    
    	int GetTreeMaxDepth();
    
    private:
    	RedBlackNode* _root;
    	RedBlackNode _nil;
    	RedBlackNodeManager _nodeManager;
    	queue<RedBlackNode*> _queue;
    	map<int, string> _numberMap;
    	map<int, string> _stickMap;
    };

     

    RedBlackTree.cpp

    #include "RedBlackTree.h"
    
    #pragma region 노드 매니저
    /// <summary>
    /// 종료 전 생성하 노드 제거
    /// </summary>
    RedBlackNodeManager::~RedBlackNodeManager()
    {
    	while (_nodes != nullptr)
    	{
    		RedBlackNode* temp{ _nodes };
    		_nodes = _nodes->left;
    		delete temp;
    	}
    }
    
    /// <summary>
    /// Nil 노드를 등록한다.
    /// </summary>
    /// <param name="nil">Nil 노드</param>
    void RedBlackNodeManager::RegisterNilNode(RedBlackNode* nil)
    {
    	_nil = nil;
    }
    
    /// <summary>
    /// 사용 완료한 노드를 매니저에 저장
    /// </summary>
    /// <param name="node">사용후 반환할 노드</param>
    void RedBlackNodeManager::Push(RedBlackNode* node)
    {
    	node->left = _nodes;
    	_nodes = node;
    }
    
    /// <summary>
    /// 노드가 필요한 경우 매니저에서 반환
    /// </summary>
    /// <returns>사용할 수 있는 노드</returns>
    RedBlackNode* RedBlackNodeManager::Pop()
    {
    	RedBlackNode* node{ _nodes };
    
    	if (node != nullptr)
    	{
    		_nodes = node->left;
    		node->Clear();
    	}
    	else
    	{
    		node = new RedBlackNode();
    	}
    
    	node->color = NodeColor::Red;
    	node->left = node->right = _nil;
    
    	return node;
    }
    
    /// <summary>
    /// 주어진 노드를 부모로 하는 공백 노드를 반환한다.
    /// </summary>
    /// <param name="parent">부모 노드</param>
    /// <returns>공백 노드</returns>
    RedBlackNode* RedBlackNodeManager ::GetEmptyNode(RedBlackNode* parent)
    {
    	RedBlackNode* emptyNode = Pop();
    	emptyNode->parent = parent;
    	emptyNode->isEmpty = true;
    
    	return emptyNode;
    }
    #pragma endregion
    
    #pragma region 레드 블랙 트리
    /// <summary>
    /// 레드 블랙 트리에 필요한 정보 세팅
    /// </summary>
    RedBlackTree::RedBlackTree()
    	: _root(nullptr)
    {
    	_nil.color = NodeColor::Black;
    	_nil.isEmpty = true;
    
    	_nodeManager.RegisterNilNode(&_nil);
    }
    
    /// <summary>
    /// 종료 전 남은 노드 제거 처리
    /// </summary>
    RedBlackTree::~RedBlackTree()
    {
    	while (_root != nullptr)
    	{
    		Delete(_root);
    	}
    }
    
    /// <summary>
    /// 레드 블랙 트리에서 특정 값을 가지는 노드가 존재하는지 여부 확인
    /// </summary>
    /// <param name="data">찾을 값</param>
    /// <returns>존재 여부</returns>
    bool RedBlackTree::Exists(int data)
    {
    	if (_root == nullptr)
    	{
    		return false;
    	}
    
    	RedBlackNode* node{ _root };
    
    	while (node != &_nil)
    	{
    		if (node->data == data)
    		{
    			return true;
    		}
    		else if (node->data > data)
    		{
    			node = node->left;
    		}
    		else
    		{
    			node = node->right;
    		}
    	}
    
    	return false;
    }
    
    /// <summary>
    /// 레드 블랙 트리에서 주어진 값을 가진 노드를 반환
    /// </summary>
    /// <param name="data">찾을 값</param>
    /// <returns>값을 가진 노드, 없으면 nullptr</returns>
    const RedBlackNode& RedBlackTree::Search(int data)
    {
    	return *GetNode(data);
    }
    
    /// <summary>
    /// 레드 블랙 트리에 값을 가지는 노드를 삽입
    /// </summary>
    /// <param name="data">삽입할 값</param>
    void RedBlackTree::Insert(int data)
    {
    	if (Exists(data))
    	{
    		return;
    	}
    
    	AdjustInsertedNode(Insert(_root, data));
    }
    
    /// <summary>
    /// 레드 블랙 트리에서 해당 값을 가지는 노드를 제거
    /// </summary>
    /// <param name="data">제거할 값</param>
    void RedBlackTree::Delete(int data)
    {
    	if (!Exists(data))
    	{
    		return;
    	}
    
    	AdjustDeletedNode(Delete(GetNode(data)));
    }
    
    /// <summary>
    /// 트리를 출력한다.
    /// </summary>
    void RedBlackTree::PrintTree()
    {
    	if (_root == nullptr)
    	{
    		std::cout << "EMPTY\n";
    		return;
    	}
    
    	int maxDepth{ _root->GetMaxDepth() };
    	int maxNodeCount{ static_cast<int>(std::pow(2, maxDepth - 1)) };
    	int totalCount{ maxNodeCount * 2 + 1 };
    	int lineWidth{ totalCount * RedBlackNode::Width };
    
    	for (int i = 1; i <= maxDepth; i++)
    	{
    		_numberMap[i] = "";
    		_stickMap[i] = "";
    	}
    
    	_queue.push(_root);
    	while (!_queue.empty())
    	{
    		RedBlackNode* node{ _queue.front() };
    		_queue.pop();
    
    		PrintTree(node, lineWidth);
    
    		if (!node->isEmpty)
    		{
    			if (node->left != &_nil)
    			{
    				_queue.push(node->left);
    			}
    			else
    			{
    				RedBlackNode* emptyNode{ _nodeManager.GetEmptyNode(node) };
    				emptyNode->left = node;
    				_queue.push(emptyNode);
    			}
    
    			if (node->right != &_nil)
    			{
    				_queue.push(node->right);
    			}
    			else
    			{
    				RedBlackNode* emptyNode{ _nodeManager.GetEmptyNode(node) };
    				emptyNode->right = node;
    				_queue.push(emptyNode);
    			}
    		}
    
    		if (node->isEmpty)
    		{
    			_nodeManager.Push(node);
    		}
    	}
    
    	for (int i = 1; i <= maxDepth; i++)
    	{
    		std::cout << _stickMap[i] << '\n';
    		std::cout << _numberMap[i] << '\n';
    	}
    	std::cout << "\n\n";
    }
    
    /// <summary>
    /// 레드 블랙 트리 삽입 처리
    /// </summary>
    /// <param name="parent">삽입해야 할 노드의 부모</param>
    /// <param name="data">삽입할 값</param>
    /// <returns>새로 삽입한 노드</returns>
    RedBlackNode* RedBlackTree::Insert(RedBlackNode* parent, int data)
    {
    	if (parent == nullptr)
    	{
    		RedBlackNode* node{ _nodeManager.Pop() };
    		node->data = data;
    		_root = node;
    
    		return node;
    	}
    	else if (parent->data > data)
    	{
    		if (parent->left == &_nil)
    		{
    			RedBlackNode* node{ _nodeManager.Pop() };
    			node->data = data;
    			node->parent = parent;
    			parent->left = node;
    
    			return node;
    		}
    		else
    		{
    			return Insert(parent->left, data);
    		}
    	}
    	else
    	{
    		if (parent->right == &_nil)
    		{
    			RedBlackNode* node{ _nodeManager.Pop() };
    			node->data = data;
    			node->parent = parent;
    			parent->right = node;
    
    			return node;
    		}
    		else
    		{
    			return Insert(parent->right, data);
    		}
    	}
    }
    
    /// <summary>
    /// 레드 블랙 트리에 삽입한 노드를 규칙에 맞게 정렬한다.
    /// </summary>
    /// <param name="node">문제가 되는 노드</param>
    void RedBlackTree::AdjustInsertedNode(RedBlackNode* node)
    {
    	RedBlackNode* x{ node };
    	RedBlackNode* p{ x->parent };
    
    	if (x == _root)
    	{
    		x->color = NodeColor::Black;
    		return;
    	}
    
    	if (p->color == NodeColor::Black)
    	{
    		return;
    	}
    
    	RedBlackNode* y{ x->GetSibling() };
    	RedBlackNode* p2{ p->parent };
    	RedBlackNode* s{ p->GetSibling() };
    
    	// case 1
    	if (s->color == NodeColor::Red)
    	{
    		p->color = s->color = NodeColor::Black;
    		p2->color = NodeColor::Red;
    		AdjustInsertedNode(p2);
    	}
    	else
    	{
    		// case 2-1
    		if (IsLeftNode(p) && IsRightNode(x))
    		{
    			RotateLeft(p);
    			// case 2-2
    			RotateRight(p2);
    		}
    		else if (IsRightNode(p) && IsLeftNode(x))
    		{
    			RotateRight(p);
    			// case 2-2
    			RotateLeft(p2);
    		}
    		// case 2-2
    		p2->SwapColor(p);
    	}
    }
    
    /// <summary>
    /// 레드 블랙 트리 제거 처리
    /// </summary>
    /// <param name="node">제거할 노드</param>
    RedBlackNode* RedBlackTree::Delete(RedBlackNode* node)
    {
    	RedBlackNode* parent{ node->parent };
    	RedBlackNode* x{ nullptr };
    
    	if (node->left == &_nil && node->right == &_nil)
    	{
    		if (IsLeftNode(node))
    		{
    			parent->left = &_nil;
    			_nil.parent = parent;
    		}
    		else if (IsRightNode(node))
    		{
    			parent->right = &_nil;
    			_nil.parent = parent;
    		}
    		else
    		{
    			_root = nullptr;
    		}
    
    		if (node->color == NodeColor::Black)
    		{
    			x = &_nil;
    		}
    
    		_nodeManager.Push(node);
    	}
    	else if (node->left != &_nil && node->right == &_nil ||
    		node->left == &_nil && node->right != &_nil)
    	{
    		RedBlackNode* child{ node->left };
    		if (child == &_nil)
    		{
    			child = node->right;
    		}
    
    		if (IsLeftNode(node))
    		{
    			parent->left = child;
    			child->parent = parent;
    		}
    		else if (IsRightNode(node))
    		{
    			parent->right = child;
    			child->parent = parent;
    		}
    		else
    		{
    			_root = child;
    			child->parent = nullptr;
    		}
    		child->parent = parent;
    
    		if (node->color == NodeColor::Black && child->color == NodeColor::Red)
    		{
    			x = child;
    		}
    
    		_nodeManager.Push(node);
    	}
    	else
    	{
    		RedBlackNode* child{ node->left };
    		while (child->right != &_nil)
    		{
    			child = child->right;
    		}
    		node->data = child->data;
    		return Delete(child);
    	}
    
    	return x;
    }
    
    /// <summary>
    /// 레드 블랙 트리에서 노드를 제거한 뒤 규칙에 맞게 정렬한다.
    /// </summary>
    /// <param name="node">문제가 되는 노드</param>
    void RedBlackTree::AdjustDeletedNode(RedBlackNode* node)
    {
    	if (node == nullptr)
    	{
    		return;
    	}
    
    	RedBlackNode* x{ node };
    	RedBlackNode* p{ x->parent };
    	RedBlackNode* s{ x->GetSibling() };
    	RedBlackNode* l{ s->left };
    	RedBlackNode* r{ s->right };
    
    	// case 1-1
    	if (p->color == NodeColor::Red && s->color == NodeColor::Black &&
    		l->color == NodeColor::Black && r->color == NodeColor::Black)
    	{
    		p->SwapColor(s);
    	}
    	// case *-2 x가 왼쪽
    	else if (s->color == NodeColor::Black && IsLeftNode(x) &&
    		r->color == NodeColor::Red)
    	{
    		RotateLeft(p);
    		r->color = NodeColor::Black;
    		p->SwapColor(s);
    	}
    	// case *-2 x가 오른쪽
    	else if (s->color == NodeColor::Black && IsRightNode(x) &&
    		l->color == NodeColor::Red)
    	{
    		RotateRight(p);
    		l->color = NodeColor::Black;
    		p->SwapColor(s);
    	}
    	// case *-3 x가 왼쪽
    	else if (s->color == NodeColor::Black && IsLeftNode(x) &&
    		l->color == NodeColor::Red && r->color == NodeColor::Black)
    	{
    		RotateRight(s);
    		l->SwapColor(s);
    		AdjustDeletedNode(x);
    	}
    	// case *-3 x가 오른쪽
    	else if (s->color == NodeColor::Black && IsRightNode(x) &&
    		l->color == NodeColor::Black && r->color == NodeColor::Red)
    	{
    		RotateLeft(s);
    		r->SwapColor(s);
    		AdjustDeletedNode(x);
    	}
    	// case 2-1
    	else if (p->color == NodeColor::Black && s->color == NodeColor::Black &&
    		l->color == NodeColor::Black && r->color == NodeColor::Black)
    	{
    		s->color = NodeColor::Red;
    		AdjustDeletedNode(p);
    	}
    	// case 2-4
    	else if (p->color == NodeColor::Black && s->color == NodeColor::Red)
    	{
    		if (IsLeftNode(x))
    		{
    			RotateLeft(p);
    		}
    		else
    		{
    			RotateRight(p);
    		}
    		p->SwapColor(s);
    		AdjustDeletedNode(x);
    	}
    	
    	_nil.parent = nullptr;
    }
    
    /// <summary>
    /// 트리를 출력한다.
    /// </summary>
    /// <param name="node">현재 출력할 노드</param>
    void RedBlackTree::PrintTree(RedBlackNode* node, int lineWidth)
    {
    	int curDepth{ node->GetCurDepth() };
    	int curNodeCount{ static_cast<int>(std::pow(2, curDepth - 1)) };
    	int totalNodeSize{ curNodeCount * RedBlackNode::Width };
    	int blankCount{ curNodeCount + 1 };
    	int totalBlankSize{ lineWidth - totalNodeSize };
    	int blankSize{ totalBlankSize / blankCount };
    
    	string& numberStr{ _numberMap[curDepth] };
    	numberStr.append(string(blankSize, ' '));
    	numberStr.append(node->ToString());
    
    	string& stickStr{ _stickMap[curDepth] };
    	stickStr.append(GetNodeStick(node, blankSize));
    }
    
    /// <summary>
    /// 주어진 노드에 맞는 막대를 만들어 반환한다.
    /// </summary>
    /// <param name="node">처리할 노드</param>
    /// <returns>막대 노드 문자열</returns>
    string RedBlackTree::GetNodeStick(RedBlackNode* node, int blankSize)
    {
    	if (node == _root)
    	{
    		return "";
    	}
    
    	int halfNodeWidth{ RedBlackNode::Width / 2 };
    	int halfBlankSize{ blankSize / 2 };
    
    	string result;
    	if ((node->isEmpty && node->left != &_nil) || IsLeftNode(node))
    	{
    		int leftSpaceCnt{ blankSize + halfNodeWidth
    			- (RedBlackNode::Width % 2 == 0 ? 1 : 0) };
    		result.append(string(leftSpaceCnt, ' '));
    		result.append(node->isEmpty ? " " : "┌");
    		int rightHypenCnt{ halfBlankSize + halfNodeWidth - 1 };
    		for (int i = 0; i < rightHypenCnt; i++)
    		{
    			result.append(node->isEmpty ? " " : "─");
    		}
    		result.append(node->isEmpty ? " " : "┘");
    	}
    	else
    	{
    		result.append(node->isEmpty ? " " : "└");
    		int leftHypenCnt{ halfBlankSize + halfNodeWidth - 1 };
    		for (int i = 0; i < leftHypenCnt; i++)
    		{
    			result.append(node->isEmpty ? " " : "─");
    		}
    		result.append(node->isEmpty ? " " : "┐");
    		result.append(string(halfNodeWidth, ' '));
    	}
    
    	return result;
    }
    
    /// <summary>
    /// 주어진 값을 가진 노드의 포인터를 반환한다.
    /// </summary>
    /// <param name="data">찾으려는 값</param>
    /// <returns>노드의 포인터</returns>
    RedBlackNode* RedBlackTree::GetNode(int data)
    {
    	if (_root == nullptr)
    	{
    		return nullptr;
    	}
    
    	RedBlackNode* node{ _root };
    
    	while (node != &_nil)
    	{
    		if (node->data == data)
    		{
    			return node;
    		}
    		else if (node->data > data)
    		{
    			node = node->left;
    		}
    		else
    		{
    			node = node->right;
    		}
    	}
    
    	return nullptr;
    }
    
    /// <summary>
    /// 해당 노드가 부모 노드의 왼쪽 자식인지 여부
    /// </summary>
    /// <param name="node">확인할 노드</param>
    /// <returns>왼쪽 자식인지 여부</returns>
    bool RedBlackTree::IsLeftNode(RedBlackNode* node)
    {
    	return node->parent != nullptr && node->parent->left == node;
    }
    
    /// <summary>
    /// 해당 노드가 부모 노드의 오른쪽 자식인지 여부
    /// </summary>
    /// <param name="node">확인할 노드</param>
    /// <returns>오른쪽 자식인지 여부</returns>
    bool RedBlackTree::IsRightNode(RedBlackNode* node)
    {
    	return node->parent != nullptr && node->parent->right == node;
    }
    
    /// <summary>
    /// 주어진 노드를 기준으로 왼쪽으로 회전한다.
    /// </summary>
    /// <param name="node">기준 노드</param>
    void RedBlackTree::RotateLeft(RedBlackNode* node)
    {
    	RedBlackNode* x{ node->right };
    	RedBlackNode* p{ node };
    	RedBlackNode* p2{ p->parent };
    
    	bool wasPLeft{ IsLeftNode(p) };
    
    	p->right = x->left;
    	x->left->parent = p;
    	x->left = p;
    	p->parent = x;
    	if (p2 != nullptr)
    	{
    		if (wasPLeft)
    		{
    			p2->left = x;
    		}
    		else
    		{
    			p2->right = x;
    		}
    	}
    	else
    	{
    		_root = x;
    	}
    	x->parent = p2;
    }
    
    /// <summary>
    /// 주어진 노드를 기준으로 오른쪽으로 회전한다.
    /// </summary>
    /// <param name="node">기준 노드</param>
    void RedBlackTree::RotateRight(RedBlackNode* node)
    {
    	RedBlackNode* x{ node->left };
    	RedBlackNode* p{ node };
    	RedBlackNode* p2{ p->parent };
    
    	bool wasPLeft{ IsLeftNode(p) };
    
    	p->left = x->right;
    	x->right->parent = p;
    	x->right = p;
    	p->parent = x;
    	if (p2 != nullptr)
    	{
    		if (wasPLeft)
    		{
    			p2->left = x;
    		}
    		else
    		{
    			p2->right = x;
    		}
    	}
    	else
    	{
    		_root = x;
    	}
    	x->parent = p2;
    }
    
    /// <summary>
    /// 트리의 최대 깊이를 반환한다.
    /// </summary>
    /// <returns>트리의 최대 깊이</returns>
    int RedBlackTree::GetTreeMaxDepth()
    {
    	return _root != nullptr ? _root->GetMaxDepth() : 0;
    }
    #pragma endregion

     

    main.cpp

    #include "Common.h"
    #include "검색트리/RedBlackTree.h"
    
    int main()
    {
    	int n = 9;
    	int* arr;
    
    	// 동작 테스트를 위한 값
    	arr = new int[n]{ 10, 5, 20, 3, 7, 15, 25, 1, 4 };
    
    	RedBlackTree tree;
    	for (int i = 0; i < n; i++)
    	{
    		tree.Insert(arr[i]);
    	}
    	tree.PrintTree();
    
    	tree.Delete(5);
    	tree.PrintTree();
    
    	tree.Delete(10);
    	tree.PrintTree();
    
    	tree.Delete(15);
    	tree.PrintTree();
    
    	delete[] arr;
    }

     

    실행결과

                                                                    _10__(B)
                                               ┌───────────────────────┘└───────────────────────┐
                                            __5__(R)                                        _20__(B)
                           ┌─────────────┘└─────────────┐                           ┌─────────────┘└─────────────┐
                        __3__(B)                    __7__(B)                    _15__(R)                    _25__(R)
               ┌───────┘└───────┐                                                                                           
            __1__(R)        __4__(R)                                                                                        
    
    
    
                                                                    _10__(B)
                                               ┌───────────────────────┘└───────────────────────┐
                                            __4__(R)                                        _20__(B)
                           ┌─────────────┘└─────────────┐                           ┌─────────────┘└─────────────┐
                        __3__(B)                    __7__(B)                    _15__(R)                    _25__(R)
               ┌───────┘                                                                                                    
            __1__(R)                                                                                                        
    
    
    
                                    __7__(B)
                         ┌────────────┘└────────────┐
                      __3__(R)                  _20__(B)
               ┌───────┘└───────┐               ┌───────┘└───────┐
            __1__(B)        __4__(B)        _15__(R)        _25__(R)
    
    
    
                                    __7__(B)
                         ┌────────────┘└────────────┐
                      __3__(R)                  _20__(B)
               ┌───────┘└───────┐                        └───────┐
            __1__(B)        __4__(B)                        _25__(R)

     

    NadanKim/Algorithm: 알고리즘 학습 및 예제 코드 작성을 위한 저장소 (github.com)

     

    NadanKim/Algorithm

    알고리즘 학습 및 예제 코드 작성을 위한 저장소. Contribute to NadanKim/Algorithm development by creating an account on GitHub.

    github.com

     

    댓글

Designed by Tistory.