B-트리, B+트리
B-트리란? 이진트리와 다르게 하나의 노드에 많은 수의 정보를 가지는 형태이다.
최대 M개의 자식을 가질 수 있는 B트리를 M차 B트리라고 부른다.
구현하는 조건은
B-트리 구현 - C
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// B-트리 노드 구조
typedef struct BTreeNode {
int* keys;
struct BTreeNode** children;
int num_keys;
bool is_leaf;
} BTreeNode;
int t_global; // 전역 변수로 차수 관리
// 노드 생성
BTreeNode* create_node(bool is_leaf) {
BTreeNode* node = (BTreeNode*)malloc(sizeof(BTreeNode));
node->is_leaf = is_leaf;
node->num_keys = 0;
node->keys = (int*)malloc(sizeof(int) * (2 * t_global - 1));
node->children = (BTreeNode**)malloc(sizeof(BTreeNode*) * (2 * t_global));
for (int i = 0; i < 2 * t_global; i++) {
node->children[i] = NULL;
}
return node;
}
// 중위 순회
void traverse(BTreeNode* root) {
if (root != NULL) {
for (int i = 0; i < root->num_keys; i++) {
if (!root->is_leaf) traverse(root->children[i]);
printf("%d ", root->keys[i]);
}
if (!root->is_leaf) traverse(root->children[root->num_keys]);
}
}
// 키 탐색
BTreeNode* search(BTreeNode* node, int key) {
int i = 0;
while (i < node->num_keys && key > node->keys[i]) i++;
if (i < node->num_keys && key == node->keys[i]) return node;
if (node->is_leaf) return NULL;
return search(node->children[i], key);
}
// 자식 분할
void split_child(BTreeNode* parent, int i, BTreeNode* y) {
int t = t_global;
BTreeNode* z = create_node(y->is_leaf);
z->num_keys = t - 1;
// 오른쪽 절반 키 복사
for (int j = 0; j < t - 1; j++)
z->keys[j] = y->keys[j + t];
// 자식 포인터도 복사
if (!y->is_leaf) {
for (int j = 0; j < t; j++)
z->children[j] = y->children[j + t];
}
y->num_keys = t - 1;
// 부모에 자식 삽입
for (int j = parent->num_keys; j >= i + 1; j--)
parent->children[j + 1] = parent->children[j];
parent->children[i + 1] = z;
// 부모에 키 삽입
for (int j = parent->num_keys - 1; j >= i; j--)
parent->keys[j + 1] = parent->keys[j];
parent->keys[i] = y->keys[t - 1];
parent->num_keys++;
}
// 삽입 (full 노드 제외)
void insert_non_full(BTreeNode* node, int key) {
int i = node->num_keys - 1;
if (node->is_leaf) {
// 오른쪽으로 이동하면서 자리 찾기
while (i >= 0 && key < node->keys[i]) {
node->keys[i + 1] = node->keys[i];
i--;
}
node->keys[i + 1] = key;
node->num_keys++;
} else {
while (i >= 0 && key < node->keys[i]) i--;
i++;
if (node->children[i]->num_keys == (2 * t_global - 1)) {
split_child(node, i, node->children[i]);
if (key > node->keys[i]) i++;
}
insert_non_full(node->children[i], key);
}
}
// 삽입 (루트 처리 포함)
void insert(BTreeNode** root_ref, int key) {
BTreeNode* root = *root_ref;
if (root->num_keys == (2 * t_global - 1)) {
BTreeNode* s = create_node(false);
s->children[0] = root;
split_child(s, 0, root);
insert_non_full(s, key);
*root_ref = s;
} else {
insert_non_full(root, key);
}
}
// 메모리 해제
void free_tree(BTreeNode* root) {
if (root != NULL) {
if (!root->is_leaf) {
for (int i = 0; i <= root->num_keys; i++)
free_tree(root->children[i]);
}
free(root->keys);
free(root->children);
free(root);
}
}
//작접 구현
int main(){
}
리프노드는 연결리스트 처럼 옆으로 연결할 수 있다!!
B+트리의 특징으로는 다음과 같습니다.
B+트리 구현 - C
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int t_global;
typedef struct BPlusTreeNode {
int* keys;
struct BPlusTreeNode** children;
int num_keys;
bool is_leaf;
struct BPlusTreeNode* next; // 리프 노드 간 연결
} BPlusTreeNode;
// 노드 생성
BPlusTreeNode* create_node(bool is_leaf) {
BPlusTreeNode* node = (BPlusTreeNode*)malloc(sizeof(BPlusTreeNode));
node->is_leaf = is_leaf;
node->num_keys = 0;
node->keys = (int*)malloc(sizeof(int) * (2 * t_global - 1));
node->children = (BPlusTreeNode**)malloc(sizeof(BPlusTreeNode*) * (2 * t_global));
for (int i = 0; i < 2 * t_global; i++) node->children[i] = NULL;
node->next = NULL;
return node;
}
// 탐색
BPlusTreeNode* search(BPlusTreeNode* root, int key) {
if (root == NULL) return NULL;
int i = 0;
while (i < root->num_keys && key > root->keys[i])
i++;
if (root->is_leaf) {
if (i < root->num_keys && root->keys[i] == key)
return root;
return NULL;
}
return search(root->children[i], key);
}
// 리프에 삽입
void insert_in_leaf(BPlusTreeNode* leaf, int key) {
int i = leaf->num_keys - 1;
while (i >= 0 && key < leaf->keys[i]) {
leaf->keys[i + 1] = leaf->keys[i];
i--;
}
leaf->keys[i + 1] = key;
leaf->num_keys++;
}
// 자식 분할
void split_child(BPlusTreeNode* parent, int index, BPlusTreeNode* child) {
int t = t_global;
BPlusTreeNode* new_child = create_node(child->is_leaf);
new_child->num_keys = t;
// 리프일 경우 키 복사 + 연결
if (child->is_leaf) {
for (int i = 0; i < t; i++)
new_child->keys[i] = child->keys[i + t - 1];
child->num_keys = t - 1;
new_child->next = child->next;
child->next = new_child;
// 부모에 중간값 추가 (리프는 중복 허용)
for (int i = parent->num_keys; i > index; i--) {
parent->keys[i] = parent->keys[i - 1];
parent->children[i + 1] = parent->children[i];
}
parent->keys[index] = new_child->keys[0]; // 리프의 첫 키
parent->children[index + 1] = new_child;
parent->num_keys++;
}
// 내부 노드 분할
else {
for (int i = 0; i < t - 1; i++)
new_child->keys[i] = child->keys[i + t];
for (int i = 0; i < t; i++)
new_child->children[i] = child->children[i + t];
new_child->num_keys = t - 1;
child->num_keys = t - 1;
for (int i = parent->num_keys; i > index; i--) {
parent->keys[i] = parent->keys[i - 1];
parent->children[i + 1] = parent->children[i];
}
parent->keys[index] = child->keys[t - 1];
parent->children[index + 1] = new_child;
parent->num_keys++;
}
}
// 삽입 (비가득 노드)
void insert_non_full(BPlusTreeNode* node, int key) {
int i = node->num_keys - 1;
if (node->is_leaf) {
insert_in_leaf(node, key);
} else {
while (i >= 0 && key < node->keys[i])
i--;
i++;
if (node->children[i]->num_keys == 2 * t_global - 1) {
split_child(node, i, node->children[i]);
if (key >= node->keys[i])
i++;
}
insert_non_full(node->children[i], key);
}
}
// 삽입 시작
void insert(BPlusTreeNode** root_ref, int key) {
BPlusTreeNode* root = *root_ref;
if (root->num_keys == 2 * t_global - 1) {
BPlusTreeNode* new_root = create_node(false);
new_root->children[0] = root;
split_child(new_root, 0, root);
insert_non_full(new_root, key);
*root_ref = new_root;
} else {
insert_non_full(root, key);
}
}
// 중위 순회 (리프만 출력)
void traverse(BPlusTreeNode* root) {
while (!root->is_leaf)
root = root->children[0];
while (root != NULL) {
for (int i = 0; i < root->num_keys; i++)
printf("%d ", root->keys[i]);
root = root->next;
}
printf("\n");
}
// 메모리 해제
void free_tree(BPlusTreeNode* node) {
if (node == NULL) return;
if (!node->is_leaf) {
for (int i = 0; i <= node->num_keys; i++)
free_tree(node->children[i]);
}
free(node->keys);
free(node->children);
free(node);
}
//직접 구현
int main(){
}
트라이 (Trie)
트라이란? 문자열의 집합을 표현하는 트리 자료구조 형태이다.
트라이의 특징은 아래와 같습니다.
트라이 구현 - C
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#define ALPHABET_SIZE 26
// Trie 노드 구조체
typedef struct TrieNode {
struct TrieNode* children[ALPHABET_SIZE];
bool isEndOfWord; // 이 노드가 단어의 끝인지 여부
} TrieNode;
// 새로운 노드 생성
TrieNode* createNode() {
TrieNode* node = (TrieNode*)malloc(sizeof(TrieNode));
node->isEndOfWord = false;
for (int i = 0; i < ALPHABET_SIZE; i++)
node->children[i] = NULL;
return node;
}
// 단어 삽입
void insert(TrieNode* root, const char* key) {
TrieNode* curr = root;
while (*key) {
int index = *key - 'a';
if (curr->children[index] == NULL)
curr->children[index] = createNode();
curr = curr->children[index];
key++;
}
curr->isEndOfWord = true;
}
// 단어 검색
bool search(TrieNode* root, const char* key) {
TrieNode* curr = root;
while (*key) {
int index = *key - 'a';
if (curr->children[index] == NULL)
return false;
curr = curr->children[index];
key++;
}
return curr != NULL && curr->isEndOfWord;
}
// 메모리 해제
void freeTrie(TrieNode* root) {
for (int i = 0; i < ALPHABET_SIZE; i++) {
if (root->children[i] != NULL)
freeTrie(root->children[i]);
}
free(root);
}
//직접 구현
int main(){
}