[ 이진 탐색 트리(Binary Search Tree) ]
자식이 최대 2개 존재할 수 있음.
작으면 왼쪽, 크면 오른쪽.
30
15 50
10 25 40 80
100
장점 : 탐색이 빠름.
예) 40을 찾을 경우,
30 < 40 30 왼쪽 버림.
50 > 40 50 오른쪽 버림.
Root : 트리의 최상위 노드.
리프노드 : 가장 끝에 있는 노드. 자식이 없는 노드.
서브트리 : 큰 트리의 일부분.
편향트리 : 한쪽으로 치우친 트리. 예) 10 - 20 - 30 - 40 - 50
편향트리가 나오는 것을 주의해야 함. 이진탐색트리의 장점이 사라짐.
Value : 실제 저장할 값.
Key : 탐색할 때 사용하는 값.
[ 전체 코드 ]
BinaryTree.h
#pragma once
#include <assert.h>
template <typename Key, typename Value>
class CBinaryTreeNode
{
template <typename Key, typename Value>
friend class CBinaryTree;
template <typename Key, typename Value>
friend class CBinaryTreeIterator;
private:
CBinaryTreeNode() // 생성자.
{
m_pLeft = nullptr;
m_pRight = nullptr;
m_pParent = nullptr;
m_pNext = nullptr;
m_pPrev = nullptr;
}
~CBinaryTreeNode() // 소멸자.
{
}
private:
CBinaryTreeNode<Key, Value>* m_pLeft;
CBinaryTreeNode<Key, Value>* m_pRight;
CBinaryTreeNode<Key, Value>* m_pParent;
CBinaryTreeNode<Key, Value>* m_pNext;
CBinaryTreeNode<Key, Value>* m_pPrev;
public:
Key first;
Value second;
};
template <typename Key, typename Value>
class CBinaryTreeIterator
{
template <typename Key, typename Value>
friend class CBinaryTree;
public:
CBinaryTreeIterator()
{
m_pNode = nullptr;
}
~CBinaryTreeIterator()
{
}
private:
CBinaryTreeNode<Key, Value>* m_pNode;
public:
bool operator == (const CBinaryTreeIterator<Key, Value>& iter) const
{
return m_pNode == iter.m_pNode;
}
bool operator != (const CBinaryTreeIterator<Key, Value>& iter) const
{
return m_pNode != iter.m_pNode;
}
void operator ++ ()
{
m_pNode = m_pNode->m_pNext;
}
void operator ++ (int)
{
m_pNode = m_pNode->m_pNext;
}
void operator -- ()
{
m_pNode = m_pNode->m_pPrev;
}
void operator -- (int)
{
m_pNode = m_pNode->m_pPrev;
}
CBinaryTreeNode<Key, Value>* operator -> () const
{
return m_pNode;
}
};
template <typename Key, typename Value>
class CBinaryTree
{
public:
CBinaryTree()
{
m_pRoot = nullptr;
m_pBegin = new Node;
m_pEnd = new Node;
// begin과 end연결.
m_pBegin->m_pNext = m_pEnd;
m_pEnd->m_pPrev = m_pBegin;
m_iSize = 0;
}
~CBinaryTree()
{
clear();
delete m_pBegin;
delete m_pEnd;
}
private:
typedef CBinaryTreeNode<Key, Value> Node;
typedef CBinaryTreeNode<Key, Value>* PNode;
public:
typedef CBinaryTreeIterator<Key, Value> iterator;
private:
PNode m_pRoot;
PNode m_pBegin;
PNode m_pEnd;
int m_iSize;
public:
// 추가 (1. 반복문 이용 2. 재귀함수 이용) >> 재귀함수 이용.
void insert(const Key& key, const Value& value)
{
if (!m_pRoot) // root가 null일 경우(처음 노드를 추가하는 경우)
{
m_pRoot = new Node; // root 생성.
m_pRoot->first = key; // key 저장.
m_pRoot->second = value; // value 저장.
// root와 end를 연결.
m_pRoot->m_pNext = m_pEnd;
m_pEnd->m_pPrev = m_pRoot;
// root와 begin을 연결.
m_pRoot->m_pPrev = m_pBegin;
m_pBegin->m_pNext = m_pRoot;
}
else
{
PNode pNewNode = insert(key, value, m_pRoot);
}
++m_iSize;
}
// 크기.
int size() const
{
return m_iSize;
}
// 비어있는지 확인.
bool empty() const
{
return m_iSize == 0;
}
// 모두 지움.
void clear()
{
PNode pNode = m_pBegin->m_pNext; // begin의 다음 노드로 설정.
while (pNode != m_pEnd) // end가 아닐 동안 실행.
{
PNode pNext = pNode->m_pNext; // 지울 노드의 next를 저장.
delete pNode; // 지움.
pNode = pNext; // 지울 노드 바꿔줌.
}
// begin과 end 연결.
m_pBegin->m_pNext = m_pEnd;
m_pEnd->m_pPrev = m_pBegin;
m_iSize = 0;
m_pRoot = nullptr;
}
// 첫 노드 리턴.
iterator begin() const
{
iterator iter;
iter.m_pNode = m_pBegin->m_pNext;
return iter;
}
// 끝 노드 리턴.
iterator end() const
{
iterator iter;
iter.m_pNode = m_pEnd;
return iter;
}
// 찾기.
iterator find(const Key& key) const
{
assert(m_iSize != 0); // 노드가 존재하지 않는 경우, 경고창을 띄우고 종료 시킴.
return find(key, m_pRoot);
}
// 삭제.
iterator erase(const Key& key)
{
assert(m_iSize != 0); // 노드가 존재하지 않는 경우, 경고창을 띄우고 종료 시킴.
iterator iter = find(key, m_pRoot); // 지울 노드가 있는 지 확인.
if (iter.m_pNode == m_pEnd) // 지울 노드를 못 찼았을 경우.
return iter; // 그냥 리턴.
// 자식이 없는 노드일 경우, 그냥 지움.
if (!iter.m_pNode->m_pLeft && !iter.m_pNode->m_pRight) // left와 right가 없으면 자식 노드가 없다는 것을 의미.
{
PNode pNext = iter.m_pNode->m_pNext; // 지울 노드의 next 저장.
PNode pPrev = iter.m_pNode->m_pPrev; // 지울 노드의 previous 저장,
// 지울 노드의 next와 prevoius를 연결.
pPrev->m_pNext = pNext;
pNext->m_pPrev = pPrev;
if (!iter.m_pNode->m_pParent) // 부모 노드가 없을 경우.
m_pRoot = nullptr; // root를 null로 초기화.
else // 부모 노드가 있을 경우.
{
if (iter.m_pNode->m_pParent->m_pLeft == iter.m_pNode) // 지울 노드가 부모의 왼쪽 자식 노드일 경우.
iter.m_pNode->m_pParent->m_pLeft = nullptr; // 지울 노드 부모의 왼쪽 자식 노드를 null로 초기화.
else // 지울 노드가 부모의 오른쪽 자식 노드일 경우.
iter.m_pNode->m_pParent->m_pRight = nullptr; // 지울 노드 부모의 오른쪽 자식 노드를 null로 초기화.
}
delete iter.m_pNode; // 노드 지움.
iter.m_pNode = pNext; // pNode를 지운 노드의 next로 지정.
--m_iSize;
return iter;
}
PNode pDeleteNode = nullptr; // 지울 노드.
bool bFindLeft = false;
if (iter.m_pNode->m_pLeft) // 지울 노드의 left가 있을 경우.
{
pDeleteNode = LeftMax(iter.m_pNode->m_pLeft); // LeftMax로 왼쪽에서 가장 큰 노드를 찾음.
bFindLeft = true;
}
else // 지울 노드의 right가 있을 경우.
{
pDeleteNode = RightMin(iter.m_pNode->m_pRight); // RightMin로 오른쪽에서 가장 작은 노드를 찾음.
}
// 지울 노드의 값으로 현재 노드를 대체.
iter.m_pNode->first = pDeleteNode->first;
iter.m_pNode->second = pDeleteNode->second;
// 지울 노드의 자식노드가 있을 경우 지울 노드의 자리를 대체.
if (bFindLeft) // 지울 노드가 왼쪽에 있을 경우.
{
if (pDeleteNode->m_pParent->m_pLeft == pDeleteNode) // 지울 노드가 부모의 왼쪽에 있을 경우.
pDeleteNode->m_pParent->m_pLeft = pDeleteNode->m_pLeft; // 지울 노드의 부모 노드와 지울 노드의 left를 연결. (지울 노드의 부모의 left를 지울 노드의 left로 설정.)
else // 지울 노드가 부모의 오른쪽에 있을 경우.
pDeleteNode->m_pParent->m_pRight = pDeleteNode->m_pLeft; // 지울 노드의 부모 노드와 지울 노드의 left를 연결. (지울 노드의 부모의 right를 지울 노드의 left로 설정.)
if (pDeleteNode->m_pLeft) // 지울 노드의 left가 있는 경우.
pDeleteNode->m_pLeft->m_pParent = pDeleteNode->m_pParent; // 지울 노드를 대체할 노드의 부모를 지울 노드의 부모와 연결. (지울 노드의 left의 부모를 지울 노드의 부모로 설정.)
}
else // 지울 노드가 오른쪽에 있을 경우.
{
if (pDeleteNode->m_pParent->m_pLeft == pDeleteNode) // 지울 노드가 부모의 왼쪽에 있을 경우.
pDeleteNode->m_pParent->m_pLeft = pDeleteNode->m_pRight; // 지울 노드의 부모 노드와 지울 노드의 right를 연결. (지울 노드의 부모의 left를 지울 노드의 right로 설정.)
else // 지울 노드가 부모의 오른쪽에 있을 경우.
pDeleteNode->m_pParent->m_pRight = pDeleteNode->m_pRight; // 지울 노드의 부모 노드와 지울 노드의 right를 연결. (지울 노드의 부모의 right를 지울 노드의 right로 설정.)
if (pDeleteNode->m_pRight) // 지울 노드의 right가 있는 경우.
pDeleteNode->m_pRight->m_pParent = pDeleteNode->m_pParent; // 지울 노드를 대체할 노드의 부모를 지울 노드의 부모와 연결. (지울 노드의 right의 부모를 지울 노드의 부모로 설정.)
}
PNode pNext = pDeleteNode->m_pNext; // 지울 노드의 next를 저장.
PNode pPrev = pDeleteNode->m_pPrev; // 지울 노드의 previous를 저장.
// 지울 노드의 next와 previous를 연결.
pNext->m_pPrev = pPrev;
pPrev->m_pNext = pNext;
if (bFindLeft) // 지울 노드가 왼쪽에 있을 경우.
iter.m_pNode = iter.m_pNode->m_pNext; // pNode를 pNode의 next로 설정.
else // 지울 노드가 오른쪽에 있을 경우.
iter.m_pNode = iter.m_pNode; // pNode를 pNode로 설정.
delete pDeleteNode; // 노드 지움.
--m_iSize;
return iter;
}
private:
PNode insert(const Key& key, const Value& value, PNode pNode)
{
// key값이 pNode(중심값)보다 작을 경우. 즉, 왼쪽에 배치하는 경우.
if (pNode->first >= key)
{
if (!pNode->m_pLeft) // 왼쪽 자식 노드가 없을 경우.
{
PNode pNewNode = new Node; // 새 노드 생성.
pNewNode->first = key; // key값 저장.
pNewNode->second = value; // value값 저장.
pNode->m_pLeft = pNewNode; // pNode의 left로 새 노드 설정.
pNewNode->m_pParent = pNode; // 새 노드의 부모로 pNode 설정.
PNode pPrev = pNode->m_pPrev; // 새 노드의 부모노드의 이전노드를 얻어옴.
pPrev->m_pNext = pNewNode; // 부모노드의 이전노드의 next를 새 노드로 설정.
pNewNode->m_pPrev = pPrev; // 새 노드의 previous를 부모노드의 이전노드로 설정.
pNewNode->m_pNext = pNode; // 새 노드의 next로 pNode 설정.
pNode->m_pPrev = pNewNode; // pNode의 previous로 새 노드 설정.
return pNewNode;
}
else // 자식 노드가 있을 경우
{
return insert(key, value, pNode->m_pLeft); // 기준 노드를 pNode의 left로 줌.
}
}
// 오른쪽에 배치할 경우
// 오른쪽 자식 노드가 없을 경우 오른쪽 자식으로 만들어준다.
if (!pNode->m_pRight)
{
PNode pNewNode = new Node; // 새 노드 생성.
pNewNode->first = key ;// key값 저장.
pNewNode->second = value ;// value값 저장.
pNode->m_pRight = pNewNode; // pNode의 right로 새 노드 설정.
pNewNode->m_pParent = pNode; // 새 노드의 부모로 pNode 설정.
PNode pNext = pNode->m_pNext; // 새 노드의 부모노드의 다음노드를 얻어옴.
pNext->m_pPrev = pNewNode; // 부모노드의 다음노드의 previous를 새 노드로 설정.
pNewNode->m_pNext = pNext; // 새 노드의 next를 부모노드의 다음노드로 설정.
pNewNode->m_pPrev = pNode; // 새 노드의 previous로 pNode 설정.
pNode->m_pNext = pNewNode;// pNode의 next로 새 노드 설정.
return pNewNode;
}
// 오른쪽 자식 노드가 있을 경우 오른쪽 자식노드를 넣어주어서 다시 탐색.
return insert(key, value, pNode->m_pRight);
}
iterator find(const Key& key, PNode pNode) const
{
if (!pNode) // 찾을 노드가 없을 경우
return end(); // end를 리턴.
else if (pNode->first == key) // 노드를 찾았을 경우.
{
iterator iter;
iter.m_pNode = pNode;
return iter;
}
if (pNode->first > key) // key값이 더 작을 경우, 왼쪽을 탐색.
return find(key, pNode->m_pLeft);
return find(key, pNode->m_pRight); // 아닐 경우, 오른쪽 탐색.
}
// 왼쪽에서 가장 큰 노드 찾기.
PNode LeftMax(PNode pNode)
{
if (!pNode->m_pRight) // 오른쪽 자식 노드가 없을 경우.
return pNode; // 현재 노드가 가장 큰 것이기 때문에 현재 노드를 리턴.
return LeftMax(pNode->m_pRight); // 아닐 경우. 오른쪽 노드를 기준으로 다시 찾음.
}
// 오른쪽에서 가장 작은 노드 찾기.
PNode RightMin(PNode pNode)
{
if (!pNode->m_pLeft) // 왼쪽 자식 노드가 없을 경우.
return pNode; // 현재 노드가 가장 작은 것이기 때문에 현재 노드를 리턴.
return RightMin(pNode->m_pLeft); // 아닐 경우. 왼쪽 노드를 기준으로 다시 찾음.
}
};
main.cpp
#include <iostream>
#include "BinaryTree.h"
int main()
{
CBinaryTree<int, const char*> tree;
int iArr[8] = { 50, 40, 80, 30, 15, 25, 10, 100 };
tree.insert(30, "가");
tree.insert(15, "나");
tree.insert(10, "다");
tree.insert(25, "라");
tree.insert(50, "마");
tree.insert(40, "바");
tree.insert(80, "사");
tree.insert(100, "아");
CBinaryTree<int, const char*>::iterator iter = tree.begin();
CBinaryTree<int, const char*>::iterator iterEnd = tree.end();
for (; iter != iterEnd; ++iter)
{
std::cout << "Key : " << iter->first << " Value : " << iter->second << std::endl;
}
std::cout << "======================= 찾기 =======================" << std::endl;
iter = tree.find(200);
if (iter == tree.end())
std::cout << "찾는 대상이 없습니다." << std::endl;
iter = tree.find(10);
std::cout << "Find Key : " << iter->first << " Value : " << iter->second << std::endl;
std::cout << "======================= 삭제 =======================" << std::endl;
iter = tree.erase(50);
std::cout << "erase 50 >> Next : " << iter->first << std::endl;
for (int i = 0; i < 8; ++i) // 50, 40, 80, 30, 15, 25, 10, 100 순으로 지움.
{
std::cout << std::endl << "Delete : " << iArr[i] << std::endl;
iter = tree.erase(iArr[i]);
if (iter != tree.end())
std::cout << "Next : " << iter->first << std::endl;
}
iter = tree.begin();
iterEnd = tree.end();
std::cout << std::endl;
for (; iter != iterEnd; ++iter)
{
std::cout << "Key : " << iter->first << " Value : " << iter->second << std::endl;
}
return 0;
}
[ 결과 ]
Key : 10 Value : 다
Key : 15 Value : 나
Key : 25 Value : 라
Key : 30 Value : 가
Key : 40 Value : 바
Key : 50 Value : 마
Key : 80 Value : 사
Key : 100 Value : 아
======================= 찾기 =======================
찾는 대상이 없습니다.
Find Key : 10 Value : 다
======================= 삭제 =======================
erase 50 >> Next : 80
Delete : 50
Delete : 40
Next : 80
Delete : 80
Next : 100
Delete : 30
Next : 100
Delete : 15
Next : 25
Delete : 25
Next : 100
Delete : 10
Next : 100
Delete : 100
[ 설명 ]
'Study > 자료구조' 카테고리의 다른 글
[자료구조/정리] 레드 블랙 트리 (0) | 2020.09.22 |
---|---|
[자료구조/정리] AVL 트리 (0) | 2020.09.22 |
[자료구조/정리] 큐(Queue) (0) | 2020.09.17 |
[자료구조/정리] 스택(Stack) (0) | 2020.09.17 |
[자료구조/정리] 배열(Array) (0) | 2020.09.16 |
댓글