Tree Structures
Introduction
Tree structures are hierarchical data structures that organize data in a parent-child relationship, where each node can have zero or more child nodes. Trees are fundamental in computer science and are used in various applications such as file systems, databases, expression parsing, and decision-making algorithms.
In this chapter, we’ll explore different types of tree structures, from basic binary trees to more complex balanced trees like AVL and red-black trees. We’ll implement these structures in C and examine their properties, operations, and performance characteristics.
Binary Trees
A binary tree is a tree data structure where each node has at most two children, referred to as the left child and the right child.
Basic Binary Tree Implementation
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct TreeNode {
int data;
struct TreeNode *left;
struct TreeNode *right;
} TreeNode;
// Create new tree node
TreeNode* create_node(int data) {
TreeNode *node = malloc(sizeof(TreeNode));
if (node == NULL) return NULL;
node->data = data;
node->left = NULL;
node->right = NULL;
return node;
}
// Free entire tree
void free_tree(TreeNode *root) {
if (root == NULL) return;
free_tree(root->left);
free_tree(root->right);
free(root);
}
// Insert node in binary tree (level-order insertion)
TreeNode* insert_level_order(TreeNode *root, int data) {
TreeNode *new_node = create_node(data);
if (new_node == NULL) return root;
if (root == NULL) {
return new_node;
}
// Use queue for level-order insertion
TreeNode *queue[100];
int front = 0, rear = 0;
queue[rear++] = root;
while (front < rear) {
TreeNode *current = queue[front++];
if (current->left == NULL) {
current->left = new_node;
return root;
} else {
queue[rear++] = current->left;
}
if (current->right == NULL) {
current->right = new_node;
return root;
} else {
queue[rear++] = current->right;
}
}
return root;
}
// Tree traversals
void inorder_traversal(TreeNode *root) {
if (root == NULL) return;
inorder_traversal(root->left);
printf("%d ", root->data);
inorder_traversal(root->right);
}
void preorder_traversal(TreeNode *root) {
if (root == NULL) return;
printf("%d ", root->data);
preorder_traversal(root->left);
preorder_traversal(root->right);
}
void postorder_traversal(TreeNode *root) {
if (root == NULL) return;
postorder_traversal(root->left);
postorder_traversal(root->right);
printf("%d ", root->data);
}
// Level-order traversal (breadth-first)
void level_order_traversal(TreeNode *root) {
if (root == NULL) return;
TreeNode *queue[100];
int front = 0, rear = 0;
queue[rear++] = root;
while (front < rear) {
TreeNode *current = queue[front++];
printf("%d ", current->data);
if (current->left != NULL) {
queue[rear++] = current->left;
}
if (current->right != NULL) {
queue[rear++] = current->right;
}
}
}
// Tree height
int tree_height(TreeNode *root) {
if (root == NULL) return 0;
int left_height = tree_height(root->left);
int right_height = tree_height(root->right);
return 1 + (left_height > right_height ? left_height : right_height);
}
// Node count
int node_count(TreeNode *root) {
if (root == NULL) return 0;
return 1 + node_count(root->left) + node_count(root->right);
}
// Leaf count
int leaf_count(TreeNode *root) {
if (root == NULL) return 0;
if (root->left == NULL && root->right == NULL) {
return 1;
}
return leaf_count(root->left) + leaf_count(root->right);
}
int main() {
TreeNode *root = NULL;
// Build tree: 1, 2, 3, 4, 5, 6, 7
for (int i = 1; i <= 7; i++) {
root = insert_level_order(root, i);
}
printf("Inorder traversal: ");
inorder_traversal(root);
printf("\n");
printf("Preorder traversal: ");
preorder_traversal(root);
printf("\n");
printf("Postorder traversal: ");
postorder_traversal(root);
printf("\n");
printf("Level-order traversal: ");
level_order_traversal(root);
printf("\n");
printf("Tree height: %d\n", tree_height(root));
printf("Node count: %d\n", node_count(root));
printf("Leaf count: %d\n", leaf_count(root));
free_tree(root);
return 0;
}Binary Search Trees (BST)
A binary search tree is a binary tree where each node follows the property that all values in the left subtree are less than the node’s value, and all values in the right subtree are greater than the node’s value.
BST Implementation
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct BSTNode {
int data;
struct BSTNode *left;
struct BSTNode *right;
} BSTNode;
// Create new BST node
BSTNode* create_bst_node(int data) {
BSTNode *node = malloc(sizeof(BSTNode));
if (node == NULL) return NULL;
node->data = data;
node->left = NULL;
node->right = NULL;
return node;
}
// Free BST
void free_bst(BSTNode *root) {
if (root == NULL) return;
free_bst(root->left);
free_bst(root->right);
free(root);
}
// Insert node in BST
BSTNode* insert_bst(BSTNode *root, int data) {
if (root == NULL) {
return create_bst_node(data);
}
if (data < root->data) {
root->left = insert_bst(root->left, data);
} else if (data > root->data) {
root->right = insert_bst(root->right, data);
}
// If data == root->data, we don't insert duplicates
return root;
}
// Search in BST
BSTNode* search_bst(BSTNode *root, int data) {
if (root == NULL || root->data == data) {
return root;
}
if (data < root->data) {
return search_bst(root->left, data);
} else {
return search_bst(root->right, data);
}
}
// Find minimum value node
BSTNode* find_min(BSTNode *root) {
if (root == NULL) return NULL;
while (root->left != NULL) {
root = root->left;
}
return root;
}
// Find maximum value node
BSTNode* find_max(BSTNode *root) {
if (root == NULL) return NULL;
while (root->right != NULL) {
root = root->right;
}
return root;
}
// Delete node from BST
BSTNode* delete_bst(BSTNode *root, int data) {
if (root == NULL) return root;
if (data < root->data) {
root->left = delete_bst(root->left, data);
} else if (data > root->data) {
root->right = delete_bst(root->right, data);
} else {
// Node to be deleted found
// Case 1: Node has no children
if (root->left == NULL && root->right == NULL) {
free(root);
return NULL;
}
// Case 2: Node has one child
if (root->left == NULL) {
BSTNode *temp = root->right;
free(root);
return temp;
}
if (root->right == NULL) {
BSTNode *temp = root->left;
free(root);
return temp;
}
// Case 3: Node has two children
BSTNode *temp = find_min(root->right);
root->data = temp->data;
root->right = delete_bst(root->right, temp->data);
}
return root;
}
// Inorder traversal (gives sorted order for BST)
void inorder_bst(BSTNode *root) {
if (root == NULL) return;
inorder_bst(root->left);
printf("%d ", root->data);
inorder_bst(root->right);
}
// Check if tree is valid BST
bool is_valid_bst(BSTNode *root, long min_val, long max_val) {
if (root == NULL) return true;
if (root->data <= min_val || root->data >= max_val) {
return false;
}
return is_valid_bst(root->left, min_val, root->data) &&
is_valid_bst(root->right, root->data, max_val);
}
bool validate_bst(BSTNode *root) {
return is_valid_bst(root, LONG_MIN, LONG_MAX);
}
int main() {
BSTNode *root = NULL;
// Insert nodes
int values[] = {50, 30, 70, 20, 40, 60, 80, 10, 25, 35, 45};
int n = sizeof(values) / sizeof(values[0]);
for (int i = 0; i < n; i++) {
root = insert_bst(root, values[i]);
}
printf("BST inorder traversal (sorted): ");
inorder_bst(root);
printf("\n");
printf("Is valid BST: %s\n", validate_bst(root) ? "Yes" : "No");
// Search operations
int search_values[] = {25, 35, 90};
for (int i = 0; i < 3; i++) {
BSTNode *found = search_bst(root, search_values[i]);
printf("Search %d: %s\n", search_values[i],
found ? "Found" : "Not found");
}
// Find min and max
BSTNode *min_node = find_min(root);
BSTNode *max_node = find_max(root);
printf("Minimum value: %d\n", min_node ? min_node->data : -1);
printf("Maximum value: %d\n", max_node ? max_node->data : -1);
// Delete operations
printf("Deleting 20...\n");
root = delete_bst(root, 20);
printf("BST after deletion: ");
inorder_bst(root);
printf("\n");
printf("Deleting 30...\n");
root = delete_bst(root, 30);
printf("BST after deletion: ");
inorder_bst(root);
printf("\n");
printf("Deleting 50...\n");
root = delete_bst(root, 50);
printf("BST after deletion: ");
inorder_bst(root);
printf("\n");
free_bst(root);
return 0;
}AVL Trees
AVL trees are self-balancing binary search trees where the heights of the two child subtrees of any node differ by at most one. This ensures O(log n) time complexity for search, insert, and delete operations.
AVL Tree Implementation
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
typedef struct AVLNode {
int data;
struct AVLNode *left;
struct AVLNode *right;
int height;
} AVLNode;
// Get height of node
int get_height(AVLNode *node) {
return node ? node->height : 0;
}
// Get balance factor
int get_balance(AVLNode *node) {
return node ? get_height(node->left) - get_height(node->right) : 0;
}
// Create new AVL node
AVLNode* create_avl_node(int data) {
AVLNode *node = malloc(sizeof(AVLNode));
if (node == NULL) return NULL;
node->data = data;
node->left = NULL;
node->right = NULL;
node->height = 1; // New node is initially at leaf level
return node;
}
// Free AVL tree
void free_avl(AVLNode *root) {
if (root == NULL) return;
free_avl(root->left);
free_avl(root->right);
free(root);
}
// Update height of node
void update_height(AVLNode *node) {
if (node != NULL) {
node->height = 1 + fmax(get_height(node->left), get_height(node->right));
}
}
// Right rotate
AVLNode* right_rotate(AVLNode *y) {
AVLNode *x = y->left;
AVLNode *T2 = x->right;
// Perform rotation
x->right = y;
y->left = T2;
// Update heights
update_height(y);
update_height(x);
// Return new root
return x;
}
// Left rotate
AVLNode* left_rotate(AVLNode *x) {
AVLNode *y = x->right;
AVLNode *T2 = y->left;
// Perform rotation
y->left = x;
x->right = T2;
// Update heights
update_height(x);
update_height(y);
// Return new root
return y;
}
// Insert node in AVL tree
AVLNode* insert_avl(AVLNode *root, int data) {
// 1. Perform normal BST insertion
if (root == NULL) {
return create_avl_node(data);
}
if (data < root->data) {
root->left = insert_avl(root->left, data);
} else if (data > root->data) {
root->right = insert_avl(root->right, data);
} else {
// Equal keys not allowed
return root;
}
// 2. Update height of this ancestor node
update_height(root);
// 3. Get balance factor
int balance = get_balance(root);
// 4. If unbalanced, perform rotations
// Left Left Case
if (balance > 1 && data < root->left->data) {
return right_rotate(root);
}
// Right Right Case
if (balance < -1 && data > root->right->data) {
return left_rotate(root);
}
// Left Right Case
if (balance > 1 && data > root->left->data) {
root->left = left_rotate(root->left);
return right_rotate(root);
}
// Right Left Case
if (balance < -1 && data < root->right->data) {
root->right = right_rotate(root->right);
return left_rotate(root);
}
// Return unchanged node pointer
return root;
}
// Find node with minimum value
AVLNode* find_min_avl(AVLNode *root) {
if (root == NULL) return NULL;
while (root->left != NULL) {
root = root->left;
}
return root;
}
// Delete node from AVL tree
AVLNode* delete_avl(AVLNode *root, int data) {
// 1. Perform standard BST deletion
if (root == NULL) return root;
if (data < root->data) {
root->left = delete_avl(root->left, data);
} else if (data > root->data) {
root->right = delete_avl(root->right, data);
} else {
// Node to be deleted found
if (root->left == NULL || root->right == NULL) {
AVLNode *temp = root->left ? root->left : root->right;
if (temp == NULL) {
temp = root;
root = NULL;
} else {
*root = *temp;
}
free(temp);
} else {
AVLNode *temp = find_min_avl(root->right);
root->data = temp->data;
root->right = delete_avl(root->right, temp->data);
}
}
if (root == NULL) return root;
// 2. Update height of current node
update_height(root);
// 3. Get balance factor
int balance = get_balance(root);
// 4. If unbalanced, perform rotations
// Left Left Case
if (balance > 1 && get_balance(root->left) >= 0) {
return right_rotate(root);
}
// Left Right Case
if (balance > 1 && get_balance(root->left) < 0) {
root->left = left_rotate(root->left);
return right_rotate(root);
}
// Right Right Case
if (balance < -1 && get_balance(root->right) <= 0) {
return left_rotate(root);
}
// Right Left Case
if (balance < -1 && get_balance(root->right) > 0) {
root->right = right_rotate(root->right);
return left_rotate(root);
}
return root;
}
// Search in AVL tree
AVLNode* search_avl(AVLNode *root, int data) {
if (root == NULL || root->data == data) {
return root;
}
if (data < root->data) {
return search_avl(root->left, data);
} else {
return search_avl(root->right, data);
}
}
// Inorder traversal
void inorder_avl(AVLNode *root) {
if (root == NULL) return;
inorder_avl(root->left);
printf("%d ", root->data);
inorder_avl(root->right);
}
// Check if tree is balanced
bool is_balanced(AVLNode *root) {
if (root == NULL) return true;
int balance = get_balance(root);
if (abs(balance) > 1) return false;
return is_balanced(root->left) && is_balanced(root->right);
}
int main() {
AVLNode *root = NULL;
// Insert nodes
int values[] = {10, 20, 30, 40, 50, 25};
int n = sizeof(values) / sizeof(values[0]);
printf("Inserting values: ");
for (int i = 0; i < n; i++) {
printf("%d ", values[i]);
root = insert_avl(root, values[i]);
}
printf("\n");
printf("AVL tree inorder traversal: ");
inorder_avl(root);
printf("\n");
printf("Is tree balanced: %s\n", is_balanced(root) ? "Yes" : "No");
printf("Root height: %d\n", get_height(root));
// Search operations
int search_values[] = {25, 35, 60};
for (int i = 0; i < 3; i++) {
AVLNode *found = search_avl(root, search_values[i]);
printf("Search %d: %s\n", search_values[i],
found ? "Found" : "Not found");
}
// Delete operations
printf("Deleting 30...\n");
root = delete_avl(root, 30);
printf("AVL tree after deletion: ");
inorder_avl(root);
printf("\n");
printf("Is tree balanced: %s\n", is_balanced(root) ? "Yes" : "No");
printf("Root height: %d\n", get_height(root));
free_avl(root);
return 0;
}Red-Black Trees
Red-black trees are another type of self-balancing binary search tree with additional properties that ensure the tree remains approximately balanced during insertions and deletions.
Red-Black Tree Properties
- Every node is either red or black
- The root is black
- All leaves (NIL nodes) are black
- If a node is red, both its children are black
- Every path from a node to its descendant leaves contains the same number of black nodes
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef enum { RED, BLACK } Color;
typedef struct RBNode {
int data;
Color color;
struct RBNode *left;
struct RBNode *right;
struct RBNode *parent;
} RBNode;
// Global NIL node (sentinel)
RBNode *NIL;
// Initialize NIL node
void init_nil(void) {
NIL = malloc(sizeof(RBNode));
NIL->color = BLACK;
NIL->left = NIL->right = NIL->parent = NULL;
}
// Create new red-black node
RBNode* create_rb_node(int data) {
RBNode *node = malloc(sizeof(RBNode));
if (node == NULL) return NULL;
node->data = data;
node->color = RED; // New nodes are red by default
node->left = node->right = node->parent = NIL;
return node;
}
// Free red-black tree
void free_rb_tree(RBNode *root) {
if (root == NULL || root == NIL) return;
free_rb_tree(root->left);
free_rb_tree(root->right);
free(root);
}
// Left rotate
void left_rotate(RBNode **root, RBNode *x) {
RBNode *y = x->right;
// Turn y's left subtree into x's right subtree
x->right = y->left;
if (y->left != NIL) {
y->left->parent = x;
}
// Link x's parent to y
y->parent = x->parent;
if (x->parent == NIL) {
*root = y;
} else if (x == x->parent->left) {
x->parent->left = y;
} else {
x->parent->right = y;
}
// Put x on y's left
y->left = x;
x->parent = y;
}
// Right rotate
void right_rotate(RBNode **root, RBNode *y) {
RBNode *x = y->left;
// Turn x's right subtree into y's left subtree
y->left = x->right;
if (x->right != NIL) {
x->right->parent = y;
}
// Link y's parent to x
x->parent = y->parent;
if (y->parent == NIL) {
*root = x;
} else if (y == y->parent->right) {
y->parent->right = x;
} else {
y->parent->left = x;
}
// Put y on x's right
x->right = y;
y->parent = x;
}
// Fixup after insertion
void insert_fixup(RBNode **root, RBNode *z) {
while (z->parent->color == RED) {
if (z->parent == z->parent->parent->left) {
RBNode *y = z->parent->parent->right;
if (y->color == RED) {
// Case 1: Uncle is red
z->parent->color = BLACK;
y->color = BLACK;
z->parent->parent->color = RED;
z = z->parent->parent;
} else {
if (z == z->parent->right) {
// Case 2: Uncle is black and z is right child
z = z->parent;
left_rotate(root, z);
}
// Case 3: Uncle is black and z is left child
z->parent->color = BLACK;
z->parent->parent->color = RED;
right_rotate(root, z->parent->parent);
}
} else {
RBNode *y = z->parent->parent->left;
if (y->color == RED) {
// Case 1: Uncle is red
z->parent->color = BLACK;
y->color = BLACK;
z->parent->parent->color = RED;
z = z->parent->parent;
} else {
if (z == z->parent->left) {
// Case 2: Uncle is black and z is left child
z = z->parent;
right_rotate(root, z);
}
// Case 3: Uncle is black and z is right child
z->parent->color = BLACK;
z->parent->parent->color = RED;
left_rotate(root, z->parent->parent);
}
}
}
(*root)->color = BLACK;
}
// Insert node in red-black tree
RBNode* insert_rb(RBNode **root, int data) {
RBNode *z = create_rb_node(data);
if (z == NULL) return NULL;
RBNode *y = NIL;
RBNode *x = *root;
// Find insertion point
while (x != NIL) {
y = x;
if (z->data < x->data) {
x = x->left;
} else {
x = x->right;
}
}
z->parent = y;
if (y == NIL) {
*root = z;
} else if (z->data < y->data) {
y->left = z;
} else {
y->right = z;
}
// Fix red-black properties
insert_fixup(root, z);
return z;
}
// Inorder traversal
void inorder_rb(RBNode *root) {
if (root == NULL || root == NIL) return;
inorder_rb(root->left);
printf("%d(%s) ", root->data, root->color == RED ? "R" : "B");
inorder_rb(root->right);
}
int main() {
init_nil();
RBNode *root = NIL;
// Insert nodes
int values[] = {7, 3, 18, 10, 22, 8, 11, 26};
int n = sizeof(values) / sizeof(values[0]);
printf("Inserting values into red-black tree: ");
for (int i = 0; i < n; i++) {
printf("%d ", values[i]);
insert_rb(&root, values[i]);
}
printf("\n");
printf("Red-black tree inorder traversal: ");
inorder_rb(root);
printf("\n");
free_rb_tree(root);
free(NIL);
return 0;
}B-Trees
B-trees are balanced tree data structures that maintain sorted data and allow for efficient insertion, deletion, and search operations. They are commonly used in databases and file systems.
B-Tree Implementation (Simplified)
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define T 3 // Minimum degree
typedef struct BTreeNode {
int n; // Number of keys
int keys[2 * T - 1]; // Array of keys
struct BTreeNode *children[2 * T]; // Array of child pointers
bool leaf; // True if node is leaf
} BTreeNode;
// Create new B-tree node
BTreeNode* create_btree_node(bool leaf) {
BTreeNode *node = malloc(sizeof(BTreeNode));
if (node == NULL) return NULL;
node->n = 0;
node->leaf = leaf;
for (int i = 0; i < 2 * T; i++) {
node->children[i] = NULL;
}
return node;
}
// Search for key in B-tree
BTreeNode* search_btree(BTreeNode *root, int key) {
int i = 0;
while (i < root->n && key > root->keys[i]) {
i++;
}
if (i < root->n && key == root->keys[i]) {
return root;
}
if (root->leaf) {
return NULL;
}
return search_btree(root->children[i], key);
}
// Split child node
void split_child(BTreeNode *parent, int i, BTreeNode *child) {
BTreeNode *z = create_btree_node(child->leaf);
z->n = T - 1;
// Copy the last T-1 keys of child to z
for (int j = 0; j < T - 1; j++) {
z->keys[j] = child->keys[j + T];
}
// Copy the last T children of child to z
if (!child->leaf) {
for (int j = 0; j < T; j++) {
z->children[j] = child->children[j + T];
}
}
child->n = T - 1;
// Shift parent's children to make space for z
for (int j = parent->n; j >= i + 1; j--) {
parent->children[j + 1] = parent->children[j];
}
parent->children[i + 1] = z;
// Shift parent's keys to make space
for (int j = parent->n - 1; j >= i; j--) {
parent->keys[j + 1] = parent->keys[j];
}
parent->keys[i] = child->keys[T - 1];
parent->n++;
}
// Insert non-full node
void insert_non_full(BTreeNode *node, int key) {
int i = node->n - 1;
if (node->leaf) {
// Find location to insert key and shift keys
while (i >= 0 && node->keys[i] > key) {
node->keys[i + 1] = node->keys[i];
i--;
}
node->keys[i + 1] = key;
node->n++;
} else {
// Find child to recurse on
while (i >= 0 && node->keys[i] > key) {
i--;
}
i++;
// Split child if it's full
if (node->children[i]->n == 2 * T - 1) {
split_child(node, i, node->children[i]);
if (node->keys[i] < key) {
i++;
}
}
insert_non_full(node->children[i], key);
}
}
// Insert key in B-tree
BTreeNode* insert_btree(BTreeNode *root, int key) {
if (root == NULL) {
root = create_btree_node(true);
root->keys[0] = key;
root->n = 1;
return root;
}
// If root is full, split it
if (root->n == 2 * T - 1) {
BTreeNode *new_root = create_btree_node(false);
new_root->children[0] = root;
split_child(new_root, 0, root);
int i = 0;
if (new_root->keys[0] < key) {
i++;
}
insert_non_full(new_root->children[i], key);
return new_root;
} else {
insert_non_full(root, key);
return root;
}
}
// Print B-tree
void print_btree(BTreeNode *root, int level) {
if (root == NULL) return;
for (int i = 0; i < level; i++) {
printf(" ");
}
printf("Level %d: [", level);
for (int i = 0; i < root->n; i++) {
printf("%d ", root->keys[i]);
}
printf("]\n");
if (!root->leaf) {
for (int i = 0; i <= root->n; i++) {
print_btree(root->children[i], level + 1);
}
}
}
int main() {
BTreeNode *root = NULL;
// Insert nodes
int values[] = {10, 20, 5, 6, 12, 30, 7, 17};
int n = sizeof(values) / sizeof(values[0]);
printf("Inserting values into B-tree: ");
for (int i = 0; i < n; i++) {
printf("%d ", values[i]);
root = insert_btree(root, values[i]);
}
printf("\n\n");
printf("B-tree structure:\n");
print_btree(root, 0);
// Search operations
int search_values[] = {6, 15, 17};
for (int i = 0; i < 3; i++) {
BTreeNode *found = search_btree(root, search_values[i]);
printf("Search %d: %s\n", search_values[i],
found ? "Found" : "Not found");
}
return 0;
}Practical Examples
File System Directory Tree
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
typedef struct DirectoryNode {
char name[100];
bool is_directory;
long size; // For files
struct DirectoryNode *first_child;
struct DirectoryNode *next_sibling;
} DirectoryNode;
// Create new directory node
DirectoryNode* create_directory_node(const char *name, bool is_dir, long size) {
DirectoryNode *node = malloc(sizeof(DirectoryNode));
if (node == NULL) return NULL;
strncpy(node->name, name, sizeof(node->name) - 1);
node->name[sizeof(node->name) - 1] = '\0';
node->is_directory = is_dir;
node->size = size;
node->first_child = NULL;
node->next_sibling = NULL;
return node;
}
// Add child to directory
void add_child(DirectoryNode *parent, DirectoryNode *child) {
if (parent->first_child == NULL) {
parent->first_child = child;
} else {
DirectoryNode *current = parent->first_child;
while (current->next_sibling != NULL) {
current = current->next_sibling;
}
current->next_sibling = child;
}
}
// Print directory tree
void print_directory_tree(DirectoryNode *root, int level) {
if (root == NULL) return;
for (int i = 0; i < level; i++) {
printf(" ");
}
if (root->is_directory) {
printf("[%s]\n", root->name);
} else {
printf("%s (%ld bytes)\n", root->name, root->size);
}
// Print children
DirectoryNode *child = root->first_child;
while (child != NULL) {
print_directory_tree(child, level + 1);
child = child->next_sibling;
}
}
// Calculate directory size
long calculate_directory_size(DirectoryNode *root) {
if (root == NULL) return 0;
if (!root->is_directory) {
return root->size;
}
long total_size = 0;
DirectoryNode *child = root->first_child;
while (child != NULL) {
total_size += calculate_directory_size(child);
child = child->next_sibling;
}
return total_size;
}
// Find file by name
DirectoryNode* find_file(DirectoryNode *root, const char *name) {
if (root == NULL) return NULL;
if (strcmp(root->name, name) == 0) {
return root;
}
// Search in children
DirectoryNode *child = root->first_child;
while (child != NULL) {
DirectoryNode *found = find_file(child, name);
if (found != NULL) {
return found;
}
child = child->next_sibling;
}
return NULL;
}
int main() {
// Create file system structure
DirectoryNode *root = create_directory_node("root", true, 0);
// Create directories
DirectoryNode *home = create_directory_node("home", true, 0);
DirectoryNode *usr = create_directory_node("usr", true, 0);
DirectoryNode *john = create_directory_node("john", true, 0);
DirectoryNode *documents = create_directory_node("documents", true, 0);
DirectoryNode *pictures = create_directory_node("pictures", true, 0);
// Create files
DirectoryNode *file1 = create_directory_node("readme.txt", false, 1024);
DirectoryNode *file2 = create_directory_node("report.pdf", false, 204800);
DirectoryNode *file3 = create_directory_node("photo.jpg", false, 512000);
DirectoryNode *file4 = create_directory_node("vacation.png", false, 1024000);
// Build tree structure
add_child(root, home);
add_child(root, usr);
add_child(home, john);
add_child(john, documents);
add_child(john, pictures);
add_child(documents, file1);
add_child(documents, file2);
add_child(pictures, file3);
add_child(pictures, file4);
// Print directory tree
printf("File System Directory Tree:\n");
print_directory_tree(root, 0);
// Calculate sizes
printf("\nDirectory Sizes:\n");
printf("Root directory size: %ld bytes\n", calculate_directory_size(root));
printf("John's directory size: %ld bytes\n", calculate_directory_size(john));
printf("Documents directory size: %ld bytes\n", calculate_directory_size(documents));
// Find files
printf("\nFile Search:\n");
DirectoryNode *found = find_file(root, "photo.jpg");
if (found) {
printf("Found file: %s (%ld bytes)\n", found->name, found->size);
} else {
printf("File not found\n");
}
return 0;
}Summary
Tree structures are essential data structures in computer science with various applications:
- Binary Trees: Basic hierarchical structure with at most two children per node
- Binary Search Trees: Ordered binary trees with efficient search, insert, and delete operations
- AVL Trees: Self-balancing BSTs that maintain O(log n) operations
- Red-Black Trees: Another self-balancing BST with relaxed balancing requirements
- B-Trees: Multi-way trees optimized for disk storage and databases
Key properties and considerations:
Time Complexity:
- BST (unbalanced): O(n) worst case, O(log n) average
- Balanced trees (AVL, Red-Black): O(log n) guaranteed
- B-Trees: O(log n) with better cache performance
Space Complexity: O(n) for n nodes
Use Cases:
- BST: Expression trees, decision trees
- AVL/Red-Black: In-memory databases, symbol tables
- B-Trees: File systems, database indexes
Understanding tree structures and their variations is crucial for efficient data organization and retrieval in complex applications.