일지
알고리즘...44
niamdank
2021. 8. 31. 01:28
AVL 트리 구현
AVLTree.h
#pragma once
#include "../Common.h"
/// <summary>
/// AVL 트리에 사용할 노드
/// </summary>
struct AVLNode
{
AVLNode() : data{ 0 }, bf{ 0 }, parent{ nullptr },
left{ nullptr }, right{ nullptr } {}
AVLNode(int data) : data{ data }, bf{ 0 }, parent{ nullptr },
left{ nullptr }, right{ nullptr } {}
void Clear()
{
data = 0;
parent = nullptr;
left = nullptr;
right = nullptr;
}
bool HasBalanceProblem()
{
return bf < -1 || 1 < bf;
}
int data;
int bf;
AVLNode* parent;
AVLNode* left;
AVLNode* right;
};
/// <summary>
/// AVL 트리에서 노드의 재활용을 위한 매니저
/// </summary>
class AVLNodeManager
{
public:
~AVLNodeManager();
void Push(AVLNode* node);
AVLNode* Pop();
private:
AVLNode* nodes;
};
/// <summary>
/// 연결 자료구조를 이용한 AVL 트리
/// </summary>
class AVLTree
{
public:
~AVLTree();
bool Exists(int data);
const AVLNode& Search(int data);
void Insert(int data);
void Delete(int data);
private:
void Insert(AVLNode* parent, int data);
void Delete(AVLNode* node);
AVLNode* GetNode(int data);
bool IsLeftNode(AVLNode* node);
bool IsRightNode(AVLNode* node);
AVLNode* RotateLeft(AVLNode* node);
AVLNode* RotateRight(AVLNode* node);
int CalculateBalaceFactor(AVLNode* node);
int GetNodeDepth(AVLNode* node, int depth = 0);
bool IsLLCase(AVLNode* c, AVLNode* c2);
bool IsRRCase(AVLNode* c, AVLNode* c2);
bool IsLRCase(AVLNode* c, AVLNode* c2);
bool IsRLCase(AVLNode* c, AVLNode* c2);
private:
AVLNode* _root;
AVLNodeManager _nodeManager;
};
AVLTree.cpp
#include "AVLTree.h"
#pragma region 노드 매니저
/// <summary>
/// 종료 전 생성하 노드 제거
/// </summary>
AVLNodeManager::~AVLNodeManager()
{
while (nodes != nullptr)
{
AVLNode* temp{ nodes };
nodes = nodes->left;
delete temp;
}
}
/// <summary>
/// 사용 완료한 노드를 매니저에 저장
/// </summary>
/// <param name="node">사용후 반환할 노드</param>
void AVLNodeManager::Push(AVLNode* node)
{
node->left = nodes;
nodes = node;
}
/// <summary>
/// 노드가 필요한 경우 매니저에서 반환
/// </summary>
/// <returns>사용할 수 있는 노드</returns>
AVLNode* AVLNodeManager::Pop()
{
AVLNode* node{ nodes };
if (node != nullptr)
{
nodes = node->left;
node->Clear();
}
else
{
node = new AVLNode();
}
return node;
}
#pragma endregion
#pragma region AVL트리
/// <summary>
/// 종료 전 남은 노드 제거 처리
/// </summary>
AVLTree::~AVLTree()
{
while (_root != nullptr)
{
Delete(_root);
}
}
/// <summary>
/// AVL 트리에서 특정 값을 가지는 노드가 존재하는지 여부 확인
/// </summary>
/// <param name="data">찾을 값</param>
/// <returns>존재 여부</returns>
bool AVLTree::Exists(int data)
{
AVLNode* node{ _root };
while (node != nullptr)
{
if (node->data == data)
{
return true;
}
else if (node->data > data)
{
node = node->left;
}
else
{
node = node->right;
}
}
return false;
}
/// <summary>
/// AVL 트리에서 주어진 값을 가진 노드를 반환
/// </summary>
/// <param name="data">찾을 값</param>
/// <returns>값을 가진 노드, 없으면 nullptr</returns>
const AVLNode& AVLTree::Search(int data)
{
return *GetNode(data);
}
/// <summary>
/// AVL 트리에 값을 가지는 노드를 삽입
/// </summary>
/// <param name="data">삽입할 값</param>
void AVLTree::Insert(int data)
{
if (Exists(data))
{
return;
}
Insert(_root, data);
CalculateBalaceFactor(_root);
}
/// <summary>
/// AVL 트리에서 해당 값을 가지는 노드를 제거
/// </summary>
/// <param name="data">제거할 값</param>
void AVLTree::Delete(int data)
{
if (!Exists(data))
{
return;
}
Delete(GetNode(data));
CalculateBalaceFactor(_root);
}
/// <summary>
/// AVL 트리 삽입 처리
/// </summary>
/// <param name="parent">삽입해야 할 노드의 부모</param>
/// <param name="data">삽입할 값</param>
void AVLTree::Insert(AVLNode* parent, int data)
{
if (parent == nullptr)
{
AVLNode* node{ _nodeManager.Pop() };
node->data = data;
_root = node;
}
else if (parent->data > data)
{
if (parent->left == nullptr)
{
AVLNode* node{ _nodeManager.Pop() };
node->data = data;
node->parent = parent;
parent->left = node;
}
else
{
Insert(parent->left, data);
}
}
else
{
if (parent->right == nullptr)
{
AVLNode* node{ _nodeManager.Pop() };
node->data = data;
node->parent = parent;
parent->right = node;
}
else
{
Insert(parent->right, data);
}
}
}
/// <summary>
/// AVL 트리 제거 처리
/// </summary>
/// <param name="node">제거할 노드</param>
void AVLTree::Delete(AVLNode* node)
{
AVLNode* parent{ node->parent };
if (node->left == nullptr && node->right == nullptr)
{
if (IsLeftNode(node))
{
parent->left = nullptr;
}
else if (IsRightNode(node))
{
parent->right = nullptr;
}
else
{
_root = nullptr;
}
_nodeManager.Push(node);
}
else if (node->left != nullptr && node->right == nullptr ||
node->left == nullptr && node->right != nullptr)
{
AVLNode* child{ node->left };
if (child == nullptr)
{
child = node->right;
}
if (IsLeftNode(node))
{
parent->left = child;
}
else if (IsRightNode(node))
{
parent->right = child;
}
else
{
_root = child;
}
child->parent = parent;
_nodeManager.Push(node);
}
else
{
AVLNode* child{ node->left };
while (child->right != nullptr)
{
child = child->right;
}
node->data = child->data;
Delete(child);
}
}
/// <summary>
/// 주어진 값을 가진 노드의 포인터를 반환한다.
/// </summary>
/// <param name="data">찾으려는 값</param>
/// <returns>노드의 포인터</returns>
AVLNode* AVLTree::GetNode(int data)
{
AVLNode* node{ _root };
while (node != nullptr)
{
if (node->data == data)
{
break;
}
else if (node->data > data)
{
node = node->left;
}
else
{
node = node->right;
}
}
return node;
}
/// <summary>
/// 해당 노드가 부모 노드의 왼쪽 자식인지 여부
/// </summary>
/// <param name="node">확인할 노드</param>
/// <returns>왼쪽 자식인지 여부</returns>
bool AVLTree::IsLeftNode(AVLNode* node)
{
return node->parent != nullptr && node->parent->left == node;
}
/// <summary>
/// 해당 노드가 부모 노드의 오른쪽 자식인지 여부
/// </summary>
/// <param name="node">확인할 노드</param>
/// <returns>오른쪽 자식인지 여부</returns>
bool AVLTree::IsRightNode(AVLNode* node)
{
return node->parent != nullptr && node->parent->right == node;
}
/// <summary>
/// 주어진 노드를 기준으로 왼쪽으로 회전한다.
/// </summary>
/// <param name="node">기준 노드</param>
AVLNode* AVLTree::RotateLeft(AVLNode* node)
{
AVLNode* x{ node };
AVLNode* c{ x->right };
c->left = x;
c->parent = x->parent;
x->parent = c;
x->right = nullptr;
return c;
}
/// <summary>
/// 주어진 노드를 기준으로 오른쪽으로 회전한다.
/// </summary>
/// <param name="node">기준 노드</param>
AVLNode* AVLTree::RotateRight(AVLNode* node)
{
AVLNode* x{ node };
AVLNode* c{ x->left };
c->right = x;
c->parent = x->parent;
x->parent = c;
x->left = nullptr;
return c;
}
/// <summary>
/// 주어진 노드의 BF를 구하여 반환한다.
/// </summary>
/// <param name="node">타겟 노드</param>
int AVLTree::CalculateBalaceFactor(AVLNode* node)
{
if (node == nullptr)
{
return 0;
}
CalculateBalaceFactor(node->left);
CalculateBalaceFactor(node->right);
node->bf = GetNodeDepth(node->left) - GetNodeDepth(node->right);
// 밸런스가 무너진 경우
if (node->HasBalanceProblem())
{
AVLNode* x{ node };
AVLNode* c{ x->left != nullptr ? x->left : x->right };
AVLNode* c2{ c->left != nullptr ? c->left : c->right };
if (IsLLCase(c, c2))
{
node = RotateRight(x);
}
else if (IsRRCase(c, c2))
{
node = RotateLeft(x);
}
else if (IsLRCase(c, c2))
{
RotateRight(c);
node = RotateLeft(x);
}
else if (IsRLCase(c, c2))
{
RotateLeft(c);
node = RotateRight(x);
}
node->bf = GetNodeDepth(node->left) - GetNodeDepth(node->right);
}
return node->bf + (node != _root ? 1 : 0);
}
/// <summary>
/// 주어진 노드의 깊이를 구하여 반환한다.
/// </summary>
/// <param name="node">시작 노드</param>
/// <returns>시작 노드의 최대 깊이</returns>
int AVLTree::GetNodeDepth(AVLNode* node, int depth)
{
if (node == nullptr)
{
return depth;
}
int leftDepth{ GetNodeDepth(node->left, depth + 1) };
int rightDepth{ GetNodeDepth(node->right, depth + 1) };
return (leftDepth > rightDepth ? leftDepth : rightDepth);
}
/// <summary>
/// 주어진 노드의 배치가 LL케이스인지 여부를 반환한다.
/// </summary>
/// <param name="c">문제 노드의 자식</param>
/// <param name="c2">c의 자식</param>
/// <returns>LL케이스인지 여부</returns>
bool AVLTree::IsLLCase(AVLNode* c, AVLNode* c2)
{
if (IsLeftNode(c) && IsLeftNode(c2))
{
return true;
}
return false;
}
/// <summary>
/// 주어진 노드의 배치가 RR케이스인지 여부를 반환한다.
/// </summary>
/// <param name="c">문제 노드의 자식</param>
/// <param name="c2">c의 자식</param>
/// <returns>RR케이스인지 여부</returns>
bool AVLTree::IsRRCase(AVLNode* c, AVLNode* c2)
{
if (IsRightNode(c) && IsRightNode(c2))
{
return true;
}
return false;
}
/// <summary>
/// 주어진 노드의 배치가 LR케이스인지 여부를 반환한다.
/// </summary>
/// <param name="c">문제 노드의 자식</param>
/// <param name="c2">c의 자식</param>
/// <returns>LR케이스인지 여부</returns>
bool AVLTree::IsLRCase(AVLNode* c, AVLNode* c2)
{
if (IsLeftNode(c) && IsRightNode(c2))
{
return true;
}
return false;
}
/// <summary>
/// 주어진 노드의 배치가 RL케이스인지 여부를 반환한다.
/// </summary>
/// <param name="c">문제 노드의 자식</param>
/// <param name="c2">c의 자식</param>
/// <returns>RL케이스인지 여부</returns>
bool AVLTree::IsRLCase(AVLNode* c, AVLNode* c2)
{
if (IsRightNode(c) && IsLeftNode(c2))
{
return true;
}
return false;
}
#pragma endregion