introduction to algorithms(Thomas H. Cormen...) 의 수도코드를 강력하게 참고했습니다
node 구조체 구현
typedef struct node{
int key;
struct node *parent;
struct node *left;
struct node *right;
} node_t
tree 초기화
node_t *root = NULL;
insert(&root, 7);
insert(&root, 3);
insert(&root, 5);
insert(&root, 9);
print_tree(root);
functions
int print_tree(node_t *curr) 함수
- 트리의 모든 노드의 key값을 오름차순으로 출력하는 함수
- recursion을 이용한 dfs
- left subtree 탐색 -> 현재 노드 출력 -> right subtree 탐색 순서
int print_tree(node_t *curr){
if (curr == NULL) return 0;
print_tree(curr->left);
printf("%d\n", curr->key);
print_tree(curr->right);
return 0;
}
node_t *search(node_t *curr, int k) 함수
- 주어진 key값을 가지는 노드를 찾아 그 주소(포인터) 반환하는 함수
- recursion을 이용한 탐색
- 주어진 key값과 노드의 key값을 비교해서 left child/ right child 노드로 이동
- 마지막 노드로 갔을때(주어진 key값이 없을 때)와 주어진 key값을 찾을 찾았을 때 탐색 종료
node_t *search(node_t *curr, int k){
if (curr == NULL){
printf("cannot find a node with value %d !!\n", k);
return curr;
}
if (curr->key == k) return curr;
if (k < curr->key) return search(curr->left, k);
else return search(curr->right, k);
}
node_t *search_iterative(node_t *curr, int k) 함수
- 바로 위 search 함수의 iterative 버전
node_t *search_iterative(node_t *curr, int k){
while (curr != NULL && curr->key != k){
if (k < curr->key) curr = curr->left;
else curr = curr->right;
}
if (curr == NULL){
printf("cannot find a node with value %d !!\n", k);
}
return curr;
}
node_t * minimum(node_t *curr) 함수
- 트리에서 최소 key값을 가지는 노드의 주소(포인터)를 반환하는 함수
- 트리에서 가장 왼쪽에 있는 노드를 반환
node_t * minimum(node_t *curr){
while (curr->left != NULL) curr = curr->left;
return curr;
}
node_t *maximum(node_t *curr) 함수
- 트리에서 최대 key값을 가지는 노드의 주소(포인터)를 반환하는 함수
- 트리에서 가장 오른쪽에 있는 노드를 반환
node_t *maximum(node_t *curr){
while (curr->right != NULL) curr = curr->right;
return curr;
}
node_t *successor(node_t *curr) 함수
- 주어진 노드의 successor 노드의 주소(포인터)를 반환하는 함수
- successor: 노드의 key들을 오름차순 정렬했을때 바로 다음에 오는 key값을 가지는 노드
- case1, 오른쪽 subtree가 비지 않았을 때: 오른쪽 subtree에서의 최소 값을 가지는 노드가 successor
- case2, 오른쪽 subtree가 비었을 때, curr의 조상 중 처음으로 left-child가 되는 노드의 부모 노드가 successor
node_t *successor(node_t *curr){
if (curr->right != NULL) return minimum(curr->right);
node_t *temp;
temp = curr->parent;
while( temp != NULL && temp->right == curr){
curr = temp;
temp = temp->parent;
}
if (temp == NULL){
printf("there is no successor of given node!\n");
}
return temp;
}
node_t *predecessor(node_t *curr) 함수
- 주어진 노드의 predecessor 노드의 주소(포인터)를 반환하는 함수
- predecessor: 노드의 key들을 오름차순 정렬했을 때 직전에 오는 key값을 가지는 노드
- successor함수와 거울 대칭으로 탐색
node_t *predecessor(node_t *curr){
if (curr->left != NULL) return maximum(curr->left);
node_t *temp;
temp = curr->parent;
while(temp != NULL && temp->left == curr){
curr = temp;
temp = temp->parent;
}
if (temp == NULL){
printf("there is no successor of given node!\n");
}
return temp;
}
int insert(node_t **ptr_root, int k) 함수
- 트리에 주어진 key값을 가지는 새로운 노드를 추가하는 함수
- 예외: 트리가 비었을 경우 새로운 노드를 root노드로 설정
- 주어진 key값과 현재 노드의 key값을 대소비교하면서 트리를 타고 내려간다
- leaf node에 도달하면 여기에 새로운 노드를 자식 노드로 추가
int insert(node_t **ptr_root, int k){
node_t * new_node = (node_t*)malloc(sizeof(node_t));
new_node->key = k;
new_node->parent = NULL;
new_node->left = NULL;
new_node->right = NULL;
if (*ptr_root == NULL){
*ptr_root = new_node;
return 0;
}
node_t *curr = *ptr_root;
node_t *before = NULL;
while (curr != NULL){
before = curr;
if (k < curr->key) curr = curr->left;
else curr = curr->right;
}
new_node->parent = before;
if (k < before->key) before->left = new_node;
else before->right = new_node;
return 1;
}
void delete(node_t **ptr_root, node_t *target) 함수
- 트리에서 주어진 노드를 삭제하는 함수
- 3가지 케이스로 나누어 삭제를 진행
- case1, target node의 자식 노드가 0개: target node 제거하고 그 위치에 NULL 추가
- case2, target node의 자식 노드가 1개: target node 제거 후 target node의 부모에 target node의 자식 노드 연결
- case3, target node의 자식 노드가 2개: target node와 그 successor의 key값을 교환하고, successor node를 제거(successor노드의 부모노드와 successor노드의 자식노드를 연결)
// target 노드의 주소값을 받아 트리에서 제거
void delete(node_t **ptr_root, node_t *target){
// introduction to algorithms 책 보고 이해하는 것 추천
////////////////////////
// 1. splice 대상 y 결정
node_t *y = NULL;
// target의 자식 노드가 0, 1개인 경우: splice 대상 y는 target 자신
if (target->left == NULL || target->right ==NULL) y = target;
// target의 자식 노드가 2개인 경우: splice 대상 y는 target 노드의 successor 노드
else y = successor(target);
/////////////////////////
// 2. y의 자식 노드 x 결정
// y가 잘려나가면 그 밑의 노드를 이어주어야 하므로
node_t *x = NULL;
if (y->left != NULL) x = y->left;
else x = y->right;
/////////////////////////
// 3. splicing y
// exception case1: x = NULL인 경우 제외
if (x != NULL) x->parent = y->parent;
// exception case2: y가 root node인 경우(y->parent == NULL)
if (y->parent == NULL) *ptr_root = x;
// y가 root node가 아닌 경우
else {
// y의 위치에 따라 x의 부모를 y의 부모로 설정
if (y->parent->left == y) y->parent->left = x;
else y->parent->right = x;
}
// target의 자식이 2개인 경우: splicing 대상 y는 target이 아니라 target의 successor
if (y != target) target->key = y->key;
// delete y
free(y);
}