이진 탐색 트리(Binary Search Tree)란?
이진트리 기반의 탐색을 위한 자료 구조로 효율적인 탐색 작업을 할 때 사용하는 자료 구조입니다.
이진 탐색 트리(Binary Search Tree)의 정의
-모든 노드는 유일한 키를 갖는다.
-왼쪽 서브 트리 키들은 루트 키보다 작다.
-오른쪽 서브 트리의 키들은 루트의 키보다 크다.
-왼쪽과 오른쪽 서브 트리도 이진 탐색 트리이다.
-탐색작업을 효율적으로 하기 위한 자료구조
이진 탐색 트리(Binary Search Tree)의 탐색 그림 예시 && C언어 코드
-비교한 결과가 같으면 탐색이 성공적으로 끝난다.
-키 값이 루트보다 작은면 왼쪽 자식을 기준으로 다시 탐색
-키 값이 루트보다 크면 오른쪽 자식을 기준으로 다시 탐색
이진 탐색 트리(Binary Search Tree)의 삽입 연산 그림 예시 && C언어 코드
먼저 탐색을 수행하며 탐색에 실패한 위치가 바로 새로운 노드를 삽입하는 위치라고 생각하면 됩니다.
이진 탐색 트리(Binary Search Tree)의 삭제 연산 그림 예시 && C언어 코드
노드 삭제의 3가지 경우
- 삭제하려는 노드가 단말 노드일 경우
- 삭제하려는 노드가 하나의 왼쪽이나 오른쪽 서브 트리 중 하나만 가지고 있는 경우
- 삭제하려는 노드가 두 개의 서브 트리 모두 가지고 있는 경우
case 1 : 단말 노드 삭제
단말 노드의 부모 노드를 찾아서 연결을 끊으면 된다. 단말 노드를 가리키는 부모 노드의 링크를 null로 저장하면 됩니다.
case 2 : 자식이 하나인 노드 삭제
노드는 삭제하고 서브 트리는 부모 노드에 붙여줍니다.
case 3 : 두 개의 자식을 가진 노드 삭제
자식을 두 개를 가진 노드를 삭제할 경우 그림으로 보이는 것처럼 오른쪽 서브 트리에서 제일 작은 값이거나 왼쪽 서브 트리에서 제일 큰 값을 삭제할 노드에 덮어 씌어주고 그 값을 삭제를 합니다.
#include<stdio.h>
#include<stdlib.h>
typedef int Telement;
typedef struct binaryTreeNode
{
Telement data;
struct binaryTreeNode *left;
struct binaryTreeNode *right;
}TNode;
typedef struct LinkedNode
{
TNode* data;
struct LinkedNode* link;
}Node;
Node* front = NULL;
Node* rear = NULL;
TNode* ROOT;
void error(char* str)
{
fprintf(stderr,"%s\n",str);
exit(1);
}
int is_empty(){ return front == NULL;}
void init_queue() { front = rear = NULL;}
int size()
{
Node *p;
int count = 0;
for(p = front; p!= NULL; p=p->link) count++;
return count;
}
void enqueue(TNode* e)
{
Node* p = (Node*)malloc(sizeof(Node));
p->data = e;
p->link = NULL;
if(is_empty()) front = rear = p;
else
{
rear->link = p;
rear = p;
}
}
TNode* dequeue()
{
Node* p;
TNode* e;
if(is_empty())
error("큐 공백 에러");
p = front;
front = front->link;
if(front == NULL) rear = NULL;
e = p->data;
free(p);
return e;
}
void init_tree(){ROOT = NULL;}
int is_empty_tree() { return ROOT == NULL;}
TNode* get_root() { return ROOT; }
TNode* create_tree(Telement val,TNode* left,TNode* right)
{
TNode* n = (TNode*)malloc(sizeof(TNode));
n->data = val;
n->left = left;
n->right = right;
return n;
}
void preorder(TNode* n)
{
if(n != NULL)
{
printf("[%d] ",n->data);
preorder(n->left);
preorder(n->right);
}
}
void inorder(TNode *n)
{
if(n != NULL)
{
inorder(n->left);
printf("[%d] ",n->data);
inorder(n->right);
}
}
void postorder(TNode* n)
{
if(n != NULL)
{
postorder(n->left);
postorder(n->right);
printf("[%d] ",n->data);
}
}
void levelorder(TNode* root)
{
TNode* n;
if(root == NULL) return;
init_queue();
enqueue(root);
while(!is_empty())
{
n = dequeue();
if(n != NULL)
{
printf("[%d] ",n->data);
enqueue(n->left);
enqueue(n->right);
}
}
}
int count_node(TNode* n)
{
if(n == NULL) return 0;
return 1 + count_node(n->left) + count_node(n->right);
}
int count_leaf(TNode* n)
{
if(n == NULL) return 0;
if(n->left == NULL && n->right == NULL) return 1;
else return count_leaf(n->left) +count_leaf(n->right);
}
int calc_height(TNode* n)
{
int hleft,hright;
if(n == NULL) return 0;
hleft = calc_height(n->left);
hright = calc_height(n->right);
return (hleft > hright) ? hleft + 1 : hright + 1;
}
TNode* search(TNode *n, Telement key)
{
if(n==NULL) return NULL; //key값을 가진 node를 찾지 못할경우 null 값을 반환한다.
else if( key == n->data ) return n; //key값을 가진 노드를 발견할 경우 해당 노드를 반환한다.
else if( key < n->data ) return search(n->left,key); //이진트리의 특성을 갖고 있으며 해당 노드의 key값보다 작으면 왼쪽 서브트리를 탐색
else return search(n->right,key); //key값보다 클 경우 오른쪽 서브트리를 탐색
}
void search_BST(Telement key)
{
TNode* n = search(ROOT,key); //root 부터 방문하여 key값을 가진 node를 찾는다.
if( n!= NULL) printf("[탐색 연산] : 성공 [%d] = 0x%x\n",n->data,n); //탐색 성공
else printf("[탐색 실패] : 실패 no %d\n",key); // 탐색 실패
}
void insert_BST(Telement key)
{
TNode* node = create_tree(key,NULL,NULL); //key을 가진 새로운 노드를 동적할당하여 만들어 준다.
if(is_empty_tree()) ROOT = node; //전역변수로 선언한 root가 null값일 경우 처음 삽입한 key을 가진 노드가 root노드가 된다.
else if(insert(ROOT,node) == 0) free(node); //root노드를 가진 경우 insert 함수로 이동
}
Telement insert(TNode* root,TNode* insert_node)
{
if(insert_node->data == root->data) return 0; //만약 이진트리에 key값을 가진 노드가 있을경우 반환값을 0으로 처리
else if(insert_node->data < root->data) //삽입할 노드의 값이 루트노드보다 작을 경우
{
if(root->left == NULL) root->left = insert_node; //루트노드의 왼쪽 자식 노드가 비어있을 경우 왼쪽 자식 노드에 삽입
else insert(root->left,insert_node); //왼쪽 노드가 있을 경우 다시 순환 재귀함수로 부모노드를 루트의 왼쪽노드로 설정한다.
}
else //삽입할 노드의 값이 루트노드보다 클 경우
{
if(root->right == NULL) root->right = insert_node;
else insert(root->right,insert_node);
}
return 1;
}
void delete(TNode *parent,TNode* delete_node)
{
TNode *child, *succ, *succp;
//case 1 : 삭제할 노드가 자식을 갖지 않는 단말 노드일 경우
if((delete_node->left == NULL && delete_node->right == NULL))
{
if(parent == NULL) ROOT = NULL; //부모 노드가 null 값이란 뜻은 삭제할 노드가 루트 노드 또는 없을 경우이기 때문에 null값을 반환
else
{
if(parent->left == delete_node) parent->left = NULL; //부모 노드의 왼쪽노드와 삭제할 노드가 같을 경우 null 값 처리
else parent->right = NULL; // 오른쪽노드와 같을경우 null 값 처리
}
}
//case 2 : 삭제할 노드가 자식이 하나를 갖는 경우
else if(delete_node->left == NULL || delete_node->right == NULL)
{
child = (delete_node->left != NULL) ? delete_node->left : delete_node->right; // 삭제할 노드가 가진 자식노드를 찾는다.
if(delete_node == ROOT) ROOT = child; //삭제할 노드가 루트 노드일 경우 루트노드를 자식 노드로 설정
else
{
if(parent->left == delete_node) parent->left = child; //자식이 하나밖에 없으므로 삭제할 노드를 해당 자식노드에 넣어준다.
else parent->right = child;
}
}
//case 3
else
{
succp = delete_node; //삭제할 노드를 부모로 설정
succ = delete_node->right; //삭제할 노드의 오른쪽 자식 노드로 설정
while (succ->left != NULL) //오른쪽 자식노드의 왼쪽 자식노드를 찾는다.(그림예시참고)
{
succp = succ;
succ = succ->left;
}
if(succp->left == succ) succp->left = succ->right; //삭제할 노드의 자식노드의 왼쪽 자식노드를 삭제과정
else succp->right = succ->right;
delete_node->data =succ->data; //삭제할 노드의 자식노드의 왼쪽 자식노드를 삭제할 노드로 설정(이진트리의 구조를 깨지 않기위해서 (그림예시참고))
delete_node = succ;
}
free(delete_node);
}
void delete_BST(Telement key)
{
TNode* parent = NULL; //삭제할 노드의 부모노드를 NULL값으로 할당
TNode* node = ROOT; //삭제할 노드를 처음에 루트노드로 설정
if(node == NULL) return; //만약 루트노드가 없을 경우 바로 null값을 반환
while (node != NULL && node->data != key) //루트노드가 존재하고 삭제할 key값을 가진 노드를 찾을 때까지 반복
{
parent = node; //부모노드를 루트노드부터 시작
node = (key< node->data) ? node->left :node->right; //해당 key값이 부모 노드기준으로 작을 경우 클 경우에 따라서 설정
}
if(node == NULL)
printf("error : key is not in the tree \n");
else delete(parent,node); //부모노드와 삭제할 노드를 delete 함수에 넣어준다.
}
'Computer Science > Data Structure' 카테고리의 다른 글
[자료구조] 그래프(Graph)란? - 컴도리돌이 (3) | 2020.01.10 |
---|---|
[자료구조] 우선순위 큐(Priority Queue)란? + 힙(heap)이란? - 컴도리돌이 (0) | 2020.01.09 |
[자료구조/트리(tree)] 중위순회,후위순회,전위순회,레벨 순회 - 컴도리돌이 (0) | 2020.01.07 |
[자료구조] 트리(Tree) 란? - 컴도리돌이 (0) | 2020.01.07 |
[자료구조] 단순 연결 리스트(singly linked list), 이중 연결리스트(double linked list) - 컴도리돌이 (1) | 2020.01.05 |