COMPREHENSION COURSE (18CSC350T)
REG NO : RA211026050020
NAME : SAKSHI SUNIL SAWANT
YEAR AND SEMESTER : III YEAR VI SEMESTER
COURSE : BTECH CSE AI AND ML
6. Tutorial on Database Management Systems: Scenario: A company wants to improve
the performance of its database system by implementing indexing. Explain how you
would design and implement B-tree indexing in C to accelerate data retrieval
operations.
Answer :
A B-tree is a self-balancing tree data structure that maintains sorted data and allows for
efficient insertion, deletion, and search operations. It is designed to work well on disk storage
systems and other environments where large amounts of data need to be stored and
accessed quickly. The structure of a B-tree consists of nodes with a certain number of keys
and pointers to child nodes, ensuring that all leaf nodes are at the same depth, thus keeping
the tree balanced.
CODE :
#include <stdio.h>
#include <stdlib.h>
#define MAX 3 // Maximum number of keys in a node
#define MIN 2 // Minimum number of keys in a node (for B-tree of order 3)
// B-tree Node structure
typedef struct BTreeNode {
int keys[MAX + 1]; // Array of keys
struct BTreeNode *children[MAX + 2]; // Array of child pointers
int key_count; // Number of keys in the node
int is_leaf; // Flag to check if the node is a leaf
} BTreeNode;
// Function to create a new B-tree node
BTreeNode *create_node(int is_leaf) {
BTreeNode *new_node = (BTreeNode *)malloc(sizeof(BTreeNode));
new_node->is_leaf = is_leaf;
new_node->key_count = 0;
for (int i = 0; i < MAX + 2; i++) {
new_node->children[i] = NULL;
}
return new_node;
}
// Function to split a full child node
void split_child(BTreeNode *parent, int i, BTreeNode *child) {
BTreeNode *new_child = create_node(child->is_leaf);
new_child->key_count = MIN - 1;
for (int j = 0; j < MIN - 1; j++) {
new_child->keys[j] = child->keys[j + MIN];
}
if (!child->is_leaf) {
for (int j = 0; j < MIN; j++) {
new_child->children[j] = child->children[j + MIN];
}
}
child->key_count = MIN - 1;
for (int j = parent->key_count; j >= i + 1; j--) {
parent->children[j + 1] = parent->children[j];
}
parent->children[i + 1] = new_child;
for (int j = parent->key_count - 1; j >= i; j--) {
parent->keys[j + 1] = parent->keys[j];
}
parent->keys[i] = child->keys[MIN - 1];
parent->key_count += 1;
}
// Function to insert a key into a non-full node
void insert_non_full(BTreeNode *node, int key) {
int i = node->key_count - 1;
if (node->is_leaf) {
while (i >= 0 && node->keys[i] > key) {
node->keys[i + 1] = node->keys[i];
i--;
}
node->keys[i + 1] = key;
node->key_count += 1;
} else {
while (i >= 0 && node->keys[i] > key) {
i--;
}
if (node->children[i + 1]->key_count == MAX) {
split_child(node, i + 1, node->children[i + 1]);
if (node->keys[i + 1] < key) {
i++;
}
}
insert_non_full(node->children[i + 1], key);
}
}
// Function to insert a key into the B-tree
void insert(BTreeNode **root, int key) {
BTreeNode *r = *root;
if (r->key_count == MAX) {
BTreeNode *s = create_node(0);
*root = s;
s->children[0] = r;
split_child(s, 0, r);
insert_non_full(s, key);
} else {
insert_non_full(r, key);
}
}
// Function to search for a key in the B-tree
BTreeNode *search(BTreeNode *root, int key) {
int i = 0;
while (i < root->key_count && key > root->keys[i]) {
i++;
}
if (i < root->key_count && key == root->keys[i]) {
return root;
}
if (root->is_leaf) {
return NULL;
}
return search(root->children[i], key);
}
// Function to print the B-tree structure (for debugging and visualization)
void print_tree(BTreeNode *root, int level) {
if (root != NULL) {
for (int i = 0; i < level; i++) {
printf(" ");
}
for (int i = 0; i < root->key_count; i++) {
printf("%d ", root->keys[i]);
}
printf("\n");
if (!root->is_leaf) {
for (int i = 0; i <= root->key_count; i++) {
print_tree(root->children[i], level + 1);
}
}
}
}
// Main function to demonstrate the B-tree operations
int main() {
BTreeNode *root = create_node(1);
int keys[] = {10, 20, 5, 6, 12, 30, 7, 17};
int n = sizeof(keys) / sizeof(keys[0]);
for (int i = 0; i < n; i++) {
insert(&root, keys[i]);
}
printf("B-tree structure:\n");
print_tree(root, 0);
int search_key = 6;
BTreeNode *search_result = search(root, search_key);
if (search_result != NULL) {
printf("Key %d found in the B-tree.\n", search_key);
} else {
printf("Key %d not found in the B-tree.\n", search_key);
}
return 0;
}
OUTPUT:
B-tree structure:
10
56
12 20 30
7 17
Key 6 found in the B-tree.
EXPLANATION:
B-tree Node Structure:
The BTreeNode structure defines a node in the B-tree, containing arrays for keys and child
pointers, the count of keys, and a flag to indicate if the node is a leaf.
Create Node Function:
create_node initializes a new B-tree node, setting its properties and allocating memory for it.
Split Child Function:
split_child splits a full child node into two nodes and adjusts the parent node to maintain the
B-tree properties.
Insert Non-full Function:
insert_non_full handles the insertion of a key into a node that is not full. It includes logic for
both
leaf and internal nodes.
Insert Function:
insert manages the overall insertion process. If the root is full, it creates a new root and splits
the old root before proceeding with the insertion.
Search Function:
search looks for a given key in the B-tree and returns the node containing the key, if found.
Print Tree Function:
print_tree is a helper function to print the structure of the B-tree, useful for debugging and
visualization