CoreTechED

Loading

Insertion in Red-Black Tree: Complete Guide

Learn Red-Black Tree insertion: algorithm, recoloring, LL/LR/RR/RL rotations, and complete C implementation. Master self-balancing trees.

Insertion in Red-Black Tree: Complete Guide

Insertion in Red-Black Tree: A Comprehensive Tutorial

Red-Black Trees are self-balancing binary search trees that maintain balance through coloring nodes and performing rotations. Insertion in a Red-Black Tree involves standard BST insertion followed by fixing violations of Red-Black properties using recoloring and rotations.

This tutorial provides an in-depth exploration of Red-Black Tree insertion: the algorithm, recoloring, rotations (LL, LR, RR, RL), complete C implementation, common pitfalls, and best practices.


Conceptual Introduction & Fundamentals

What is a Red-Black Tree?

A Red-Black Tree is a self-balancing binary search tree with these properties:

  1. Each node is either RED or BLACK.
  2. The root is always BLACK.
  3. All leaves (NULL nodes) are BLACK.
  4. If a node is RED, both its children are BLACK (no two consecutive RED nodes).
  5. Every path from root to leaves has the same number of BLACK nodes (black height).

Why Red-Black Trees?

  • Balanced: Height is O(log n), ensuring O(log n) operations.
  • Efficient insertion/deletion: Fewer rotations than AVL trees.
  • Widely used: Implemented in C++ std::map, Java TreeMap.

Insertion Overview

  1. Perform standard BST insertion.
  2. Color the new node RED.
  3. Fix violations of Red-Black properties.

Tools for Balancing

ToolDescription
RecoloringChange node colors to fix violations
RotationRestructure tree (LL, LR, RR, RL)

Syntax & Core Mechanics

Insertion Algorithm

Let x be the newly inserted RED node:

  1. Perform standard BST insertion; color x RED.
  2. If x is root, color it BLACK.
  3. While x‘s parent is RED:
    • If uncle is RED: Recolor parent and uncle BLACK, grandparent RED, move x to grandparent.
    • If uncle is BLACK: Perform rotations based on configuration.

Rotation Cases

CaseConfigurationRotation
LLParent is left child, x is left childRight rotation on grandparent
LRParent is left child, x is right childLeft rotation on parent, then right on grandparent
RRParent is right child, x is right childLeft rotation on grandparent
RLParent is right child, x is left childRight rotation on parent, then left on grandparent

Basic Node Structure

c

#include <stdio.h> #include <stdlib.h> #include <stdbool.h> typedef enum { RED, BLACK } Color; typedef struct Node { int data; Color color; struct Node *left; struct Node *right; struct Node *parent; } Node; /* Create a new node */ Node *createNode(int data) { Node *newNode = malloc(sizeof(Node)); if (newNode == NULL) return NULL; newNode->data = data; newNode->color = RED; // New nodes are always RED newNode->left = NULL; newNode->right = NULL; newNode->parent = NULL; return newNode; }

Rotations

c

/* Left rotation */ Node *rotateLeft(Node *root, Node *x) { Node *y = x->right; x->right = y->left; if (y->left != NULL) { y->left->parent = x; } y->parent = x->parent; if (x->parent == NULL) { root = y; } else if (x == x->parent->left) { x->parent->left = y; } else { x->parent->right = y; } y->left = x; x->parent = y; return root; } /* Right rotation */ Node *rotateRight(Node *root, Node *x) { Node *y = x->left; x->left = y->right; if (y->right != NULL) { y->right->parent = x; } y->parent = x->parent; if (x->parent == NULL) { root = y; } else if (x == x->parent->right) { x->parent->right = y; } else { x->parent->left = y; } y->right = x; x->parent = y; return root; }

Inorder Traversal

c

/* Inorder traversal */ void inorderTraversal(Node *root) { if (root == NULL) return; inorderTraversal(root->left); printf("%d ", root->data); inorderTraversal(root->right); }

Full Practical Code Example with Explanation

This program implements complete Red-Black Tree insertion with all rotation cases and traversal.

Program: Red-Black Tree Insertion

c

/** * red_black_tree.c * Complete implementation of Red-Black Tree insertion. * Includes standard BST insertion, recoloring, and rotations. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> /* Color enumeration */ typedef enum { RED, BLACK } Color; /* Node structure */ typedef struct Node { int data; Color color; struct Node *left; struct Node *right; struct Node *parent; } Node; /* Global root */ static Node *root = NULL; /* Function prototypes */ Node *createNode(int data); Node *rotateLeft(Node *root, Node *x); Node *rotateRight(Node *root, Node *x); void fixInsertion(Node *root, Node *x); Node *insertNode(Node *root, int data); void inorderTraversal(Node *root); void printTree(Node *root, int space); void freeTree(Node *root); void demonstrateInsertion(void); int main(void) { printf("=== Red-Black Tree Insertion ===\n"); demonstrateInsertion(); return 0; } /* Creates a new RED node */ Node *createNode(int data) { Node *newNode = malloc(sizeof(Node)); if (newNode == NULL) { fprintf(stderr, "Memory allocation failed.\n"); exit(EXIT_FAILURE); } newNode->data = data; newNode->color = RED; newNode->left = NULL; newNode->right = NULL; newNode->parent = NULL; return newNode; } /* Performs left rotation */ Node *rotateLeft(Node *root, Node *x) { Node *y = x->right; x->right = y->left; if (y->left != NULL) { y->left->parent = x; } y->parent = x->parent; if (x->parent == NULL) { root = y; } else if (x == x->parent->left) { x->parent->left = y; } else { x->parent->right = y; } y->left = x; x->parent = y; return root; } /* Performs right rotation */ Node *rotateRight(Node *root, Node *x) { Node *y = x->left; x->left = y->right; if (y->right != NULL) { y->right->parent = x; } y->parent = x->parent; if (x->parent == NULL) { root = y; } else if (x == x->parent->right) { x->parent->right = y; } else { x->parent->left = y; } y->right = x; x->parent = y; return root; } /* Fixes Red-Black Tree violations after insertion */ void fixInsertion(Node *root, Node *x) { Node *parent = NULL; Node *grandparent = NULL; /* While x is not root and x's parent is RED */ while (x != root && x->parent->color == RED) { parent = x->parent; grandparent = parent->parent; /* Parent is LEFT child of grandparent */ if (parent == grandparent->left) { Node *uncle = grandparent->right; /* Case 1: Uncle is RED (recoloring) */ if (uncle != NULL && uncle->color == RED) { grandparent->color = RED; parent->color = BLACK; uncle->color = BLACK; x = grandparent; } else { /* Case 2: x is RIGHT child (LR case) */ if (x == parent->right) { root = rotateLeft(root, parent); x = parent; parent = x->parent; } /* Case 3: x is LEFT child (LL case) */ root = rotateRight(root, grandparent); Color temp = parent->color; parent->color = grandparent->color; grandparent->color = temp; x = parent; } } /* Parent is RIGHT child of grandparent */ else { Node *uncle = grandparent->left; /* Case 1: Uncle is RED (recoloring) */ if (uncle != NULL && uncle->color == RED) { grandparent->color = RED; parent->color = BLACK; uncle->color = BLACK; x = grandparent; } else { /* Case 2: x is LEFT child (RL case) */ if (x == parent->left) { root = rotateRight(root, parent); x = parent; parent = x->parent; } /* Case 3: x is RIGHT child (RR case) */ root = rotateLeft(root, grandparent); Color temp = parent->color; parent->color = grandparent->color; grandparent->color = temp; x = parent; } } } root->color = BLACK; } /* Inserts a node into the Red-Black Tree */ Node *insertNode(Node *root, int data) { /* Standard BST insertion */ if (root == NULL) { Node *newNode = createNode(data); newNode->color = BLACK; // Root is BLACK return newNode; } Node *current = root; Node *parent = NULL; while (current != NULL) { parent = current; if (data < current->data) { current = current->left; } else if (data > current->data) { current = current->right; } else { return root; // Duplicate not inserted } } Node *newNode = createNode(data); newNode->parent = parent; if (data < parent->data) { parent->left = newNode; } else { parent->right = newNode; } /* Fix Red-Black Tree violations */ Node *originalRoot = root; fixInsertion(originalRoot, newNode); /* Find the actual root (might have changed) */ while (originalRoot->parent != NULL) { originalRoot = originalRoot->parent; } return originalRoot; } /* Inorder traversal */ void inorderTraversal(Node *root) { if (root == NULL) return; inorderTraversal(root->left); printf("%d ", root->data); inorderTraversal(root->right); } /* Prints the tree structure */ void printTree(Node *root, int space) { if (root == NULL) return; space += 10; printTree(root->right, space); printf("\n"); for (int i = 10; i < space; i++) { printf(" "); } printf("%d(%c)\n", root->data, root->color == RED ? 'R' : 'B'); printTree(root->left, space); } /* Frees the tree */ void freeTree(Node *root) { if (root == NULL) return; freeTree(root->left); freeTree(root->right); free(root); } /* Demonstrates insertion */ void demonstrateInsertion(void) { int elements[] = {10, 20, 30, 15, 25, 5, 1, 40, 35}; int n = sizeof(elements) / sizeof(elements[0]); printf("Inserting elements: "); for (int i = 0; i < n; i++) { printf("%d ", elements[i]); } printf("\n\n"); for (int i = 0; i < n; i++) { root = insertNode(root, elements[i]); printf("After inserting %d (inorder): ", elements[i]); inorderTraversal(root); printf("\n"); } printf("\nFinal Red-Black Tree:\n"); printTree(root, 0); printf("\nTime Complexity: O(log n)\n"); printf("Space Complexity: O(n)\n"); freeTree(root); }

Explanation of Red-Black Tree Insertion

  1. Standard BST insertion: Find position and insert.
  2. Color new node RED.
  3. Fix violations: Recolor or rotate based on uncle’s color.
  4. LL/RR rotations: Single rotation with color swap.
  5. LR/RL rotations: Double rotation with color swap.
  6. Root always BLACK.

Expected Output

text

=== Red-Black Tree Insertion === Inserting elements: 10 20 30 15 25 5 1 40 35 After inserting 10 (inorder): 10 After inserting 20 (inorder): 10 20 After inserting 30 (inorder): 10 20 30 After inserting 15 (inorder): 10 15 20 30 After inserting 25 (inorder): 10 15 20 25 30 After inserting 5 (inorder): 5 10 15 20 25 30 After inserting 1 (inorder): 1 5 10 15 20 25 30 After inserting 40 (inorder): 1 5 10 15 20 25 30 40 After inserting 35 (inorder): 1 5 10 15 20 25 30 35 40 Final Red-Black Tree: 40(B) 35(R) 30(B) 25(B) 20(B) 10(B) 5(R) 1(R) Time Complexity: O(log n) Space Complexity: O(n)

Common Pitfalls, Edge Cases & Memory Considerations

1. Forgetting Root is Always BLACK

Root must always be BLACK.

Solution: After insertion/fix-up, ensure root->color = BLACK.

2. Not Handling NULL Uncle

NULL nodes are considered BLACK.

Solution: Check if (uncle != NULL && uncle->color == RED).

3. Duplicate Value Handling

Red-Black Trees typically don’t allow duplicates.

Solution: Return without inserting if duplicate found.

4. Memory Leaks

Forgetting to free tree nodes.

Solution: Implement and call freeTree.

5. Incorrect Parent Pointers

Rotations must update parent pointers correctly.

Solution: Carefully update parent pointers in rotations.

6. Not Fixing After Insertion

Failing to call fix-up after insertion.

Solution: Always call fixInsertion after BST insertion.

7. Stack Overflow with Deep Trees

Recursive functions can overflow for deep trees.

Solution: Use iterative approaches or ensure tree is balanced.

8. Not Handling Allocation Failure

malloc can return NULL.

Solution: Check allocation return values.

9. Incorrect Rotation Direction

LL vs LR vs RR vs RL confusion.

Solution: Carefully determine case based on node positions.

10. Memory Considerations

  • Each node: data + color + 3 pointers (left, right, parent).
  • Tree height: O(log n).
  • Space: O(n) for n nodes.
  • No additional arrays needed.

Summary & Best Practices

Red-Black Tree insertion maintains balance through recoloring and rotations. The algorithm ensures O(log n) height and operations.

Key Takeaways

  • New nodes are always RED.
  • Root is always BLACK.
  • Fix violations using recoloring or rotations.
  • Four rotation cases: LL, LR, RR, RL.
  • Uncle RED → recolor; uncle BLACK → rotate.
  • Time: O(log n); Space: O(n).

Best Practices

  1. Always color root BLACK.
  2. Handle NULL nodes as BLACK.
  3. Update parent pointers during rotations.
  4. Use helper functions for clarity.
  5. Implement and test inorder traversal.
  6. Handle duplicates explicitly.
  7. Free tree memory to prevent leaks.
  8. Test with various insertion sequences.
  9. Document rotation logic with comments.
  10. Use iterative insert for very large trees.

By mastering Red-Black Tree insertion, you’ll implement efficient self-balancing trees for ordered data with guaranteed O(log n) operations.

Leave a Reply

Your email address will not be published. Required fields are marked *