📒 이진 탐색 트리(Binary Search Tree)
📌 이진 탐색 트리(Binary Search Tree)란?
- 이진 탐색 트리는 이진 트리 기반의 탐색을 위한 자료구조이다.
- 효율적인 탐색을 위해 고안된 자료구조이다.
📌 이진 탐색 트리 규칙
- 모든 노드의 key는 중복되지 않는다.
- 노드는 왼쪽 자식 노드보다 항상 크다.
- 노드는 오른쪽 자식 노드보다 항상 작다.
📌 탐색 트리 특성 비교
일반 이진 탐색 특성
- 중앙값을 알아야 하므로
배열에서만 사용할 수 있다.
- 연결 리스트는 이진 탐색하기에 적합하지 않다.
- 배열의 크기가 정적이어야 한다.
이진 탐색 트리 특성
📌 이진 탐색 트리의 탐색 과정
- 루트 노드부터 시작하여 찾을 값과 크기를 비교하며 결과에 따라 방향을 결정하여 내려온다.
- 찾으려는 값 < 현재 노드의 값 : 왼쪽 자식 노드로
- 찾으려는 값 > 현재 노드의 값 : 오른쪽 자식 노드로
- 일치하는 값을 찾을 때까지 1, 2 과정을 반복한다.
📌 이진 탐색 트리의 탐색 삽입
- 이진 탐색 트리의 규칙을 유지해야 하므로 새 노드가 삽입될 때 또한 이진 탐색으로 적합한 위치를 찾아야 한다.
- 탐색 과정과 마찬가지의 과정으로 위치를 찾는다.
- 더 이상 내려갈 곳이 없으면 리프 노드의 자식으로 추가된다.
📌 이진 탐색 트리의 탐색 삭제
삭제할 노드의 자식이 0개일 때
- 자식이 0개인 경우에는 아무런 문제 없이 삭제할 수 있다.
삭제할 노드의 자식이 1개일 때
- 자식이 1개인 경우에는 자식을 삭제한 노드의 부모와 연결해 주어야 한다.
삭제할 노드의 자식이 2개일 때
- 자식이 2개인 경우에는 자식을 삭제한 노드의 부모 노드와 연결하게 되면 이진 탐색 트리의 규칙이 무너지게 된다.
- 때문에, 삭제한 노드와 가장 근사한 값을 찾아 연결해 주어야 한다.
- 그렇다면, 삭제한 노드의 왼쪽 서브 트리에서 가장 큰 값과, 오른쪽 서브 트리에서 가장 작은 값 중, 어떤 것을 선택하여도 문제가 없다.
📌 이진 탐색 트리의 탐색 순회
📝 이진 탐색 트리 구현(C++)
#pragma once
struct Node {
int Data = 0;
Node* Parent = nullptr;
Node* LeftChild = nullptr;
Node* RightChild = nullptr;
};
class BinarySearchTree
{
private:
Node* mRoot;
public:
BinarySearchTree() {
mRoot = nullptr;
}
void Insert(int data);
Node* Search(Node* node, int data);
void Replace(Node* u, Node* v);
void Delete(int data);
void Delete(Node* node);
Node* Min(Node* node);
Node* Max(Node* node);
Node* Next(Node* node);
void Print(Node* node);
Node* GetRoot();
};
#include <iostream>
#include "BinarySearchTree.h"
using namespace std;
void BinarySearchTree::Insert(int data)
{
Node* newNode = new Node;
newNode->Data = data;
if (!mRoot)
{
mRoot = newNode;
return;
}
Node* node = mRoot;
Node* parent = nullptr;
while (node)
{
parent = node;
if (parent->Data > data)
node = parent->LeftChild;
else
node = parent->RightChild;
}
newNode->Parent = parent;
if (data < parent->Data)
parent->LeftChild = newNode;
else
parent->RightChild = newNode;
}
Node* BinarySearchTree::Search(Node* node, int data)
{
if (!node || node->Data == data)
return node;
if (node->Data > data)
return Search(node->LeftChild, data);
else
return Search(node->RightChild, data);
}
void BinarySearchTree::Replace(Node* deleteNode, Node* attachNode)
{
if (!deleteNode->Parent)
mRoot = attachNode;
else if (deleteNode->Parent->LeftChild == deleteNode)
deleteNode->Parent->LeftChild = attachNode;
else
deleteNode->Parent->RightChild = attachNode;
if (attachNode)
attachNode->Parent = deleteNode->Parent;
delete deleteNode;
}
void BinarySearchTree::Delete(int data)
{
Node* deleteNode = Search(mRoot, data);
Delete(deleteNode);
}
void BinarySearchTree::Delete(Node* node)
{
if (!node)
return;
if (!node->LeftChild)
Replace(node, node->RightChild);
else if (!node->RightChild)
Replace(node, node->LeftChild);
else
{
Node* next = Next(node);
node->Data = next->Data;
Delete(next);
}
}
Node* BinarySearchTree::Min(Node* node)
{
if (!node)
return nullptr;
while (node->LeftChild)
node = node->LeftChild;
return node;
}
Node* BinarySearchTree::Max(Node* node)
{
if (!node)
return nullptr;
while (node->RightChild)
node = node->RightChild;
return node;
}
Node* BinarySearchTree::Next(Node* node)
{
if (node->RightChild)
return Min(node->RightChild);
Node* parent = node->Parent;
while (parent && node == parent->RightChild)
{
node = parent;
parent = node->Parent;
}
return parent;
}
void BinarySearchTree::Print(Node* node)
{
if (!node)
return;
Print(node->LeftChild);
cout << node->Data << " ";
Print(node->RightChild);
}
Node* BinarySearchTree::GetRoot()
{
return mRoot;
}
#include "BinarySearchTree.h"
int main() {
BinarySearchTree bst;
bst.Insert(25);
bst.Insert(40);
bst.Insert(10);
bst.Delete(40);
bst.Insert(50);
bst.Insert(35);
bst.Insert(30);
bst.Delete(10);
bst.Print(bst.GetRoot());
return 0;
}
Output:
25 30 35 50