728x90
반응형
6. 이진 탐색 트리
- 이진 트리 기반의 탐색을 위한 자료 구조이다.
- 탐색은 레코드(record)의 집합에서 특정한 레코드를 찾아내는 작업을 의미
- 레코드는 하나 이상의 필드(field)로 구성된다.
- 레코드들의 집합을 테이블(table)이라고 한다.
- 레코드들을 구분할 수 있는 값을 주요 키(primary key)라고 한다.
- 이진 탐색 트리는 위와 같은 탐색 작업을 효율적으로 하기 위한 자료 구조이다.
1) 이진 탐색 트리의 정의
- 모든 노드의 키는 유일하다.
- 왼쪽 서브 트리의 키들은 루트의 키보다 작다.
- 오른쪽 서브 트리의 키들은 루트의 키보다 크다.
- 왼쪽과 오른쪽 서브 트리도 이진 탐색 트리이다.
2) 순환적인 탐색 함수
TreeNode *search(TreeNode *node, int key)
{
if( node == NULL ) return NULL;
if( key == node->key ) return node;
else if( key < node->key )
return search(node->left, key);
else
return search(node->right, key);
}
3) 반복적인 탐색 함수
TreeNode *search(TreeNode *node, int key)
{
while(node != NULL){
if( key == node->key ) return node;
else if( key < node->key )
node = node->left;
else
node = node->right;
}
return NULL; // 탐색에 실패했을 경우 NULL 반환
}
※ 효율성을 따지면 반복적인 함수가 훨씬 우수하다.
※ 함수의 매개 변수 node를 직접 사용하였는데, 사실 매개 변수는 원본 포인터의 복사본이므로 변경하여도 호출한 함수에는 별 영향이 없다.
4) 이진 트리 삽입 연산
void insert_node(TreeNode **root, int key)
{
TreeNode *p, *t; // p는 부모노드, t는 현재노드
TreeNode *n; // n은 새로운 노드
t = *root;
p = NULL;
// 탐색을 먼저 수행
while (t != NULL) {
if (key == t->key) return;
p = t;
if (key < t->key) t = t->left;
else t = t->right;
}
// key가 트리 안에 없으므로 삽입 가능
n = (TreeNode *)malloc(sizeof(TreeNode));
if (n == NULL) return;
// 데이터 복사
n->key = key;
n->left = n->right = NULL;
// 부모 노드와 링크 연결
if (p != NULL)
if (key < p->key)
p->left = n;
else p->right = n;
else *root = n;
}
5) 이진 트리 삭제 연산
(1) 단말 노드일 경우
(2) 하나의 서브 트리만 가지고 있는 경우
(3) 두 개의 서브 트리를 가지고 있는 경우 : 가장 값이 비슷한 노드를 후계자로 선택하는데, 왼쪽 서브 트리의 가장 오른쪽 값 or 오른쪽 서브 트리의 가장 왼쪽 값
void delete_node(TreeNode **root, int key)
{
TreeNode *p, *child, *succ, *succ_p, *t;
// key를 갖는 노드 t를 탐색, p는 t의 부모노드
p = NULL;
t = *root;
// key를 갖는 노드 t를 탐색한다.
while( t != NULL && t->key != key )
{
p = t;
t = ( key < t->key ) ? t->left : t->right;
}
// 탐색이 종료된 시점에 t가 NULL이면 트리안에 key가 없음
if( t == NULL )
{ // 탐색트리에 없는 키
printf("key is not in the tree");
return;
}
// 첫번째 경우: 단말노드인 경우
if( (t->left==NULL) && (t->right==NULL) )
{
if( p != NULL ){
// 부모노드의 자식필드를 NULL로 만든다.
if( p->left == t )
p->left = NULL;
else p->right = NULL;
}
else // 만약 부모노드가 NULL이면 삭제되는 노드가 루트
*root = NULL;
}
// 두번째 경우: 하나의 자식만 가지는 경우
else if((t->left==NULL)||(t->right==NULL))
{
child = (t->left != NULL) ? t->left : t->right;
if( p != NULL ){
if( p->left == t ) // 부모를 자식과 연결
p->left = child;
else p->right = child;
}
else // 만약 부모노드가 NULL이면 삭제되는 노드가 루트
*root = child;
}
// 세번째 경우: 두개의 자식을 가지는 경우
else
{
// 오른쪽 서브트리에서 후계자를 찾는다.
succ_p = t;
succ = t->right;
// 후계자를 찾아서 계속 왼쪽으로 이동한다.
while(succ->left != NULL){
succ_p = succ;
succ = succ->left;
}
// 후속자의 부모와 자식을 연결
if( succ_p->left == succ )
succ_p->left = succ->right;
else
succ_p->right = succ->right;
// 후속자가 가진 키값을 현재 노드에 복사
t->key = succ->key;
// 원래의 후속자 삭제
t = succ;
}
free(t);
}
[Reference]
- 천인국, 공용해, 하상호, 「C언어로 쉽게 풀어쓴 자료구조」, 생능출판, 2009년
반응형
'DataStructure' 카테고리의 다른 글
Sorting(정렬) (0) | 2022.03.11 |
---|---|
Priority Queue(우선순위 큐)와 Heap(힙) (0) | 2022.03.11 |
Tree(트리) - 1 [Binary Tree] (0) | 2022.03.04 |
Queue(큐) - 2 [Deque(덱)] (0) | 2022.03.04 |
Queue(큐) - 1 [Array Queue, Linked Queue] (0) | 2022.03.04 |