Skip to main content

Command Palette

Search for a command to run...

Tree Data Structure - Complete Question Bank with Answers

Updated
101 min read

SHORT ANSWER TYPE QUESTIONS


Question 1: Expression Tree Construction

Construct the expression tree for the following expression: [WBUT 2010] E = (2a + 5b)(x - 7y)^4

Answer:

An expression tree is a binary tree where:

  • Leaf nodes contain operands (variables/constants)

  • Internal nodes contain operators

  • Each subtree represents an expression

For the expression E = (2a + 5b)(x - 7y)^4, the expression tree is:

           *
          / \
         /   \
        +     ^
       / \   / \
      /   \ /   \
     *    *  -   4
    / \  / \ / \
   2   a 5 b x  *
               / \
              7   y

Explanation:

  1. The main operator is multiplication (*) between (2a + 5b) and (x - 7y)^4

  2. Left subtree represents (2a + 5b) with '+' as root

  3. Right subtree represents (x - 7y)^4 with '^' as root

  4. The exponent 4 is a leaf node on the right of '^'

  5. (x - 7y) forms a subtree with '-' as root, and 7*y as right subtree


Question 2: Non-recursive In-order Traversal of Threaded Binary Tree

Write an algorithm for non-recursive in-order traversal of a threaded binary tree. [WBUT 2011, 2018]

Answer:

Algorithm for In-order Traversal of Threaded Binary Tree:

Step 1: Start from the leftmost node (by following left pointers until NULL)
Step 2: While current node ≠ NULL, do:
    a) Visit the current node
    b) If the current node has a right thread (no right child), 
       follow the thread to the inorder successor
    c) Else (has right child), move to the right child and then 
       follow left pointers until NULL

Pseudo-code:

Procedure Inorder_Threaded(root)
Begin
    current = find_leftmost(root)  // Go to leftmost node
    
    while current ≠ NULL do
        visit(current)              // Process current node
        
        // Check if right pointer is a thread
        if current→right_is_thread = TRUE then
            current = current→right  // Follow thread to successor
        else
            // Move to right child and then leftmost
            current = current→right
            current = find_leftmost(current)
        end if
    end while
End

Function find_leftmost(node)
Begin
    while node→left ≠ NULL AND node→left_is_thread = FALSE do
        node = node→left
    end while
    return node
End

Time Complexity: O(n) - each node visited exactly once Space Complexity: O(1) - no stack required


Question 3: C Function to Find In-order Successor

Write a C language function to find the In-order successor of the root of a binary tree. [WBUT 2011]

Answer:

// Structure of a binary tree node
struct Node {
    int data;
    struct Node* left;   // pointer to left subtree
    struct Node* right;  // pointer to right subtree
    struct Node* parent; // pointer to parent (optional, used in some implementations)
};

// Method 1: Without using parent pointer
struct Node* successor(struct Node* root) {
    if (root == NULL)
        return NULL;
    
    // Case 1: If root has a right child
    if (root->right != NULL) {
        // Move to right node
        struct Node* current = root->right;
        
        // Move to the extreme left
        while (current->left != NULL)
            current = current->left;
        
        return current;
    }
    
    // Case 2: If no right child, successor is not in the subtree
    // This requires parent pointer or searching from root
    return NULL;
}

// Method 2: Function to find minimum key in a tree
struct Node* treeMinimum(struct Node* root) {
    /* returns a pointer to the minimum key in a NON-EMPTY tree */
    while (root->left != NULL)
        root = root->left;
    return root;
}

// Method 3: Using parent pointer (assuming parent links are maintained)
struct Node* treeSuccessor(struct Node* root) {
    /* returns pointer to inorder successor of root */
    struct Node* y;
    
    // Case 1: If right subtree exists, successor is minimum in right subtree
    if (root->right != NULL)
        return treeMinimum(root->right);
    
    // Case 2: Otherwise, successor is the nearest ancestor whose left child 
    // is also an ancestor of root
    y = root->parent;
    while (y != NULL && root == y->right) {
        root = y;
        y = y->parent;
    }
    return y;
}

Explanation of In-order Successor:

  • Case 1: If node has right subtree → successor is the leftmost node in right subtree

  • Case 2: If node has no right subtree → successor is the nearest ancestor for which this node lies in its left subtree


Question 4: Algorithm to Test if Binary Tree is BST

Write an algorithm to test whether a given binary tree is a binary search tree. [WBUT 2011]

Answer:

Algorithm 1: Using Min-Max Range (Efficient - O(n))

Algorithm: isBST(root, min, max)
Input: root node, minimum allowed value, maximum allowed value
Output: TRUE if tree is BST, FALSE otherwise

if root == NULL then
    return TRUE
end if

// Check if current node violates BST property
if root→data ≤ min OR root→data ≥ max then
    return FALSE
end if

// Recursively check left and right subtrees with updated ranges
// Left subtree must have values < root→data (max = root→data)
// Right subtree must have values > root→data (min = root→data)
return isBST(root→left, min, root→data) AND 
       isBST(root→right, root→data, max)

Initial call: isBST(root, -∞, +∞)

Algorithm 2: Using In-order Traversal

Algorithm: isBST_Inorder(root)
Begin
    static prev = NULL  // Keep track of previous node in inorder
    
    if root == NULL then
        return TRUE
    end if
    
    // Check left subtree
    if isBST_Inorder(root→left) == FALSE then
        return FALSE
    end if
    
    // Check current node with previous node
    if prev != NULL AND root→data ≤ prev→data then
        return FALSE
    end if
    prev = root
    
    // Check right subtree
    return isBST_Inorder(root→right)
End

Algorithm 3: Detailed Step-by-Step (Bubbling up Min-Max)

Algorithm: checkBST(root)
Step 1: If root has left child:
        Get min and max values for left subtree recursively
        If min_left ≥ root→data OR max_left ≥ root→data:
            Return "Not BST"
        
Step 2: If no left child:
        Set min value = root→data
        
Step 3: If root has right child:
        Get min and max values for right subtree recursively
        If min_right ≤ root→data OR max_right ≤ root→data:
            Return "Not BST"
        
Step 4: If no right child:
        Set max value = root→data from step 1
        
Step 5: Return (min from step 1/2, max from step 4)

Time Complexity: O(n) - each node visited once Space Complexity: O(h) where h is height of tree (recursion stack)


Question 5: Left Rotate Algorithm

Write an algorithm to left rotate a binary tree. [WBUT 2011]

Answer:

Left Rotation is an operation on a binary tree that changes the structure without changing the in-order traversal. It's used in self-balancing trees like AVL and Red-Black trees.

Algorithm: rotateLeft(u)
Input: A binary tree node u (may be a subtree of a larger tree)
Output: A new binary tree obtained by performing left rotation about u

Step 1: If u is empty OR u has no right child:
        Return empty tree (rotation not possible)
        
Step 2: Let v = right child of u
        
Step 3: Make left subtree of v as the new right subtree of u
        
Step 4: Make u as the new left child of v
        
Step 5: Update parent pointers (if parent links are maintained)
        a) Set parent of u to v
        b) If u had a parent p, make v as child of p instead of u
        
Step 6: Return v as the new root of this subtree

Pseudo-code:

BinTree rotateLeft(BinTree u) {
    if (u == NULL || rightSubtree(u) == NULL)
        return NULL;
    
    Node* v = u->right;                 // v is right child of u
    Node* temp = v->left;                // left subtree of v
    
    // Perform rotation
    v->left = u;                         // Make u left child of v
    u->right = temp;                      // Left subtree of v becomes right child of u
    
    // Update parent pointers (if used)
    if (temp != NULL)
        temp->parent = u;
    
    v->parent = u->parent;
    u->parent = v;
    
    // Update the parent of u (if u was not root)
    if (v->parent != NULL) {
        if (v->parent->left == u)
            v->parent->left = v;
        else
            v->parent->right = v;
    }
    
    return v;                             // v becomes new root of subtree
}

Diagram of Left Rotation:

    Before Rotation:          After Rotation:
        u                        v
       / \                      / \
      T1  v                    u   T3
         / \                  / \
        T2  T3               T1  T2

Properties of Left Rotation:

  • Maintains in-order traversal order

  • Time Complexity: O(1)

  • Used in AVL trees, Red-Black trees, and Splay trees


Question 6: Binary Tree Construction from Traversals

What is binary tree? Construct a binary tree using the Inorder and Postorder traversal given below: [WBUT 2012] Inorder: DBFEAGCLJHK Postorder: DFEBGLJKHCA

Answer:

Definition of Binary Tree: A binary tree is either empty or consists of one single vertex called the root, together with two disjoint binary trees called left subtree and right subtree. Each node can have at most two children (0, 1, or 2). Binary trees are used to implement binary search trees, binary heaps, and expression trees.

Construction from Inorder and Postorder:

Step-by-step construction:

  1. Find root from Postorder: Last element in postorder is the root → A

  2. Split Inorder using root:

    • Left of A in inorder: D B F E → Left subtree of A

    • Right of A in inorder: G C L J H K → Right subtree of A

  3. Process Left Subtree (DBFE):

    • From postorder, elements before A: D F E B

    • Root of left subtree: Last of these is B

    • Inorder for left subtree: D B F E

    • Split around B: Left of B: D, Right of B: F E

  4. Process B's left (D):

    • D is leaf (no children)
  5. Process B's right (FE):

    • From remaining postorder: D F E

    • Root: E

    • Inorder for this subtree: F E

    • Split around E: Left: F, Right: empty

  6. Process F: F is leaf

  7. Process Right Subtree (GCLJHK):

    • Root from postorder (after left subtree elements): C

    • Inorder for right subtree: G C L J H K

    • Split around C: Left: G, Right: L J H K

  8. Process C's left (G): G is leaf

  9. Process C's right (LJHK):

    • From postorder remaining: G L J K H

    • Root: H

    • Inorder for this subtree: L J H K

    • Split around H: Left: L J, Right: K

  10. Process H's left (LJ):

    • From postorder: G L J

    • Root: J

    • Inorder: L J

    • Split around J: Left: L, Right: empty

  11. Process J's left (L): L is leaf

  12. Process H's right (K): K is leaf

Final Binary Tree:

        A
       / \
      B   C
     / \   \
    D   E   H
       /   / \
      F   J   K
         /
        L

Question 7: AVL Tree Construction

Construct an AVL tree using the below list. Show all the steps: [WBUT 2012] 12, 11, 13, 10, 09, 15, 14, 18, 7, 6, 5, 4

Answer:

AVL Tree Definition: An AVL tree is a self-balancing binary search tree where the heights of left and right subtrees of every node differ by at most 1.

Step-by-step construction:

Step 1: Insert 12

    12 (0)

Step 2: Insert 11

    12 (1)
   /
 11 (0)

Balance Factor of 12 = 1 (OK)

Step 3: Insert 13

    12 (0)
   /  \
 11(0) 13(0)

Step 4: Insert 10

      12 (1)
     /  \
   11(1) 13(0)
   /
 10(0)

Step 5: Insert 09

        12 (2) ← UNBALANCED (BF=2)
       /  \
     11(2) 13(0)
     /
   10(1)
   /
 09(0)

Rotation Required: Right Rotation (RR) at 11 After rotation:

       12 (1)
      /  \
    10(1) 13(0)
    / \
  09(0)11(0)

Step 6: Insert 15

       12 (1)
      /  \
    10(1) 13(1)
    / \     \
  09(0)11(0) 15(0)

Step 7: Insert 14

       12 (2) ← UNBALANCED
      /  \
    10(1) 13(2)
    / \     \
  09(0)11(0) 15(1)
            /
          14(0)

Rotation Required: Right-Left Rotation (RL) at 13 After RL rotation:

       12 (1)
      /  \
    10(1) 14(1)
    / \   / \
  09(0)11(0)13(0)15(0)

Step 8: Insert 18

       12 (1)
      /  \
    10(1) 14(2)
    / \   / \
  09(0)11(0)13(0)15(1)
                  \
                  18(0)

Step 9: Insert 7

       12 (2) ← UNBALANCED
      /  \
    10(2) 14(2)
    / \   / \
  09(1)11(0)13(0)15(1)
  /              \
07(0)            18(0)

Rotation Required: Left-Right Rotation (LR) at 10 After LR rotation:

        12 (1)
       /  \
     09(1) 14(2)
    / \    / \
  07(0)10(0)13(0)15(1)
        \        \
        11(0)    18(0)

Step 10: Insert 6

        12 (2) ← UNBALANCED
       /  \
     09(2) 14(2)
    / \    / \
  07(1)10(0)13(0)15(1)
  /     \        \
06(0)  11(0)    18(0)

Rotation Required: Right Rotation (RR) at 9 After rotation:

        12 (1)
       /  \
     07(1) 14(2)
    / \    / \
  06(0)09(1)13(0)15(1)
       / \        \
     08(0)10(0)   18(0)
           \
           11(0)

Step 11: Insert 5

        12 (2) ← UNBALANCED
       /  \
     07(2) 14(2)
    / \    / \
  06(1)09(1)13(0)15(1)
  /   / \        \
05(0)08(0)10(0)  18(0)
           \
           11(0)

Rotation Required: Right Rotation (RR) at 7 After rotation:

        12 (1)
       /  \
     06(1) 14(2)
    / \    / \
  05(0)07(1)13(0)15(1)
       / \        \
     08(0)09(1)   18(0)
           / \
        10(0)11(0)

Step 12: Insert 4

        12 (2) ← UNBALANCED
       /  \
     06(2) 14(2)
    / \    / \
  05(1)07(1)13(0)15(1)
  /   / \        \
04(0)08(0)09(1)  18(0)
         / \
      10(0)11(0)

Rotation Required: Right Rotation (RR) at 6 Final AVL Tree:

        07 (1)
       /  \
     06(1) 12(1)
    /     /  \
  05(0) 09(1)14(1)
  /     / \  / \
04(0) 08(0)10(0)13(0)15(1)
                \
                18(0)

Question 8: Maximum Nodes in Binary Tree

Prove that the maximum number of nodes in a binary tree of depth k is 2^k - 1. [WBUT 2014]

Answer:

Statement: The maximum number of nodes in a binary tree of depth k is 2^k - 1.

Proof:

Step 1: Nodes at each level

  • In a binary tree, each node can have at most 2 children

  • Level 0 (root): maximum 2^0 = 1 node

  • Level 1: maximum 2^1 = 2 nodes

  • Level 2: maximum 2^2 = 4 nodes

  • Level i: maximum 2^i nodes

Step 2: Total nodes from level 0 to level (k-1) For a tree of depth k, levels are 0, 1, 2, ..., (k-1)

Maximum nodes = Sum of nodes at each level = 2^0 + 2^1 + 2^2 + ... + 2^(k-1)

Step 3: Geometric series formula This is a geometric progression with:

  • First term a = 1

  • Common ratio r = 2

  • Number of terms n = k

Sum = a × (r^n - 1)/(r - 1) = 1 × (2^k - 1)/(2 - 1) = 2^k - 1

Step 4: Verification by induction

Base case: k = 1 (depth 1 means only root level) Maximum nodes = 2^1 - 1 = 1 ✓

Inductive hypothesis: Assume true for depth = k

Inductive step: For depth = k+1 Maximum nodes = Nodes in first k levels + Maximum nodes in level k = (2^k - 1) + 2^k = 2 × 2^k - 1 = 2^(k+1) - 1

Hence proved.

Example: A full binary tree of depth 3 (levels 0, 1, 2):

  • Level 0: 1 node

  • Level 1: 2 nodes

  • Level 2: 4 nodes Total = 1 + 2 + 4 = 7 = 2^3 - 1


Question 9: Expression Tree

For the following expression draw the corresponding expression tree: [WBUT 2014] a + b*c - d/e

Answer:

Expression Tree Construction Rules:

  1. Operands (a, b, c, d, e) become leaf nodes

  2. Operators (+, -, *, /) become internal nodes

  3. Operator precedence determines tree structure:

    • Multiplication (*) and division (/) have higher precedence than addition (+) and subtraction (-)

    • Evaluation is from left to right for same precedence

Expression: a + b*c - d/e

Step-by-step construction:

Step 1: Identify operator precedence

  • b*c has highest precedence

  • d/e has next highest precedence

  • a + (result) - (result) has lowest precedence

Step 2: Build subtrees for higher precedence operations

  • Subtree for b*c: root is *, children are b and c

  • Subtree for d/e: root is /, children are d and e

Step 3: Combine with addition and subtraction

  • The expression is (a) + (b*c) - (d/e)

  • Subtraction (-) is the main operator (lowest precedence, rightmost among same precedence)

  • But note: a + bc - d/e means (a + (bc)) - (d/e)

Final Expression Tree:

         -
       /   \
      +     /
     / \   / \
    a   *  d  e
       / \
      b   c

Alternative representation (considering left-to-right evaluation):

         -
       /   \
      +     /
     / \   / \
    a   *  d  e
       / \
      b   c

Inorder Traversal: a + b c - d / e (with parentheses for clarity) Preorder Traversal: - + a b c / d e Postorder Traversal: a b c * + d e / -


Question 10: B-Tree Properties

a) Does a B-tree grow at its leaves or at its root? Why? [WBUT 2016] b) In deleting a key from a B-tree, when is it necessary to combine nodes?

Answer:

a) B-tree Growth Direction:

A B-tree grows at its root (bottom-up), not at its leaves.

Reasoning:

  • B-trees are balanced search trees designed for disk storage

  • New keys are always inserted into leaf nodes

  • When a leaf node becomes full (exceeds maximum keys), it splits

  • The middle key moves up to the parent node

  • This splitting can propagate upwards

  • If the root splits, a new root is created, increasing tree height

Why this is beneficial:

  1. Balance is maintained: All leaves remain at the same level

  2. No rotations needed: Unlike AVL trees, no complex rotations

  3. Disk I/O efficiency: Minimizes disk accesses by keeping height low

  4. Predictable performance: Search time remains O(log n)

Example of growth:

Initial: [10]
Insert 20: [10,20]
Insert 30:     [20]
              /    \
           [10]    [30]  ← Height increases at root

b) When to Combine Nodes in B-tree Deletion:

Node combination (merging) is necessary when deleting a key causes underflow - a node has fewer than the minimum required keys.

Conditions for merging:

  1. When deleting from a leaf node:

    • If after deletion, the node has < ⌈m/2⌉ keys (minimum for non-root)

    • And the adjacent sibling has exactly minimum keys (can't borrow)

  2. When deleting from an internal node:

    • If the node has < ⌈m/2⌉ keys after deletion/borrowing

    • Or when borrowing from child causes underflow

Steps when merging is required:

  1. Identify the deficient node

  2. Merge it with an adjacent sibling

  3. Pull down the separating key from the parent

  4. If parent becomes deficient, process continues upward

Example (B-tree of order 5, minimum keys = 2):

Before deletion:    [30, 50]
                   /    |    \
              [10,20] [40] [60,70] ← [40] has only 1 key (minimum)
              
After deleting 20 from left sibling:
              [30, 50]
                   |    \
              [10] [40] [60,70] ← [10] now deficient (0 keys)
              
Merge [10] with [40] using 30 from parent:
                    [50]
                   /    \
              [10,30,40] [60,70]

Question 11: Binary Tree Construction from Post-order and Pre-order

The post-order and in-order traversal sequences are given below. Construct the binary tree. [WBUT 2016] Post-order: D G E B H I F C A In-order: D B G E A C H F I

Answer:

Step-by-step construction:

Step 1: Identify root from post-order

  • Last element in post-order is root → A

Step 2: Split in-order using root

  • In-order: D B G E A C H F I

  • Left subtree of A: D B G E

  • Right subtree of A: C H F I

Step 3: Process left subtree (D B G E)

  • From post-order (excluding A): D G E B H I F C

  • Elements of left subtree in post-order: D G E B (last is B)

  • Root of left subtree: B

  • In-order for left subtree: D B G E

  • Split around B: Left: D, Right: G E

Step 4: Process B's left (D)

  • D is leaf (no children in its subtree)

Step 5: Process B's right (G E)

  • From remaining post-order for this subtree: D G E

  • Root: E (last in this group)

  • In-order for this subtree: G E

  • Split around E: Left: G, Right: empty

Step 6: Process E's left (G)

  • G is leaf

Step 7: Process right subtree (C H F I)

  • From post-order remaining: H I F C

  • Root of right subtree: C (last in this group)

  • In-order for right subtree: C H F I

  • Split around C: Left: empty, Right: H F I

Step 8: Process C's right (H F I)

  • From post-order remaining: H I F

  • Root: F (last in this group)

  • In-order for this subtree: H F I

  • Split around F: Left: H, Right: I

Step 9: Process F's left (H)

  • H is leaf

Step 10: Process F's right (I)

  • I is leaf

Final Binary Tree:

            A
           / \
          B   C
         / \   \
        D   E   F
           /   / \
          G   H   I

Verification:

  • In-order (LNR): D, B, G, E, A, C, H, F, I ✓

  • Post-order (LRN): D, G, E, B, H, I, F, C, A ✓


Question 12: B-Tree of Order 4 Construction

Construct one B-Tree of order 4 with the following data: [WBUT 2016] 34, 67, 89, 90, 100, 2, 36, 76, 53, 51, 12, 10, 77, 69

Answer:

B-Tree Properties (Order m = 4):

  • Maximum keys per node = m - 1 = 3

  • Minimum keys per node (except root) = ⌈m/2⌉ - 1 = 1

  • Each node can have 4 children maximum

  • All leaves at same level

Step-by-step construction:

Step 1: Insert 34, 67, 89

[34, 67, 89]

Step 2: Insert 90 Node becomes full (4 keys). Split at median (67):

    [67]
   /    \
[34]   [89, 90]

Step 3: Insert 100 Insert into rightmost leaf:

    [67]
   /    \
[34]   [89, 90, 100]

Step 4: Insert 2 Insert into left leaf:

        [67]
       /    \
[2, 34]   [89, 90, 100]

Step 5: Insert 36 Insert into left leaf (sorted):

        [67]
       /    \
[2, 34, 36] [89, 90, 100]

Step 6: Insert 76 Insert into right leaf (but need to check position):

        [67]
       /    \
[2,34,36]  [76,89,90,100] → Full!

Split right leaf at median (89):

        [67, 89]
       /   |    \
[2,34,36] [76] [90,100]

Step 7: Insert 53 53 goes between 36 and 67, so in left leaf's right child? Actually, 53 < 67, so goes in left subtree, but > 36, so becomes new leaf:

        [67, 89]
       /   |    \
[2,34,36] [53,76] [90,100]

Step 8: Insert 51 Insert into [53,76]:

        [67, 89]
       /   |    \
[2,34,36] [51,53,76] [90,100]

Step 9: Insert 12 Insert into [2,34,36]:

        [67, 89]
       /   |    \
[2,12,34,36] [51,53,76] [90,100] → Full!

Split left leaf at median (34):

           [34, 67, 89]
         /    |    |    \
    [2,12] [36] [51,53,76] [90,100]

Step 10: Insert 10 Insert into [2,12]:

           [34, 67, 89]
         /    |    |    \
    [2,10,12] [36] [51,53,76] [90,100]

Step 11: Insert 77 Insert into [51,53,76]:

           [34, 67, 89]
         /    |    |    \
    [2,10,12] [36] [51,53,76,77] [90,100] → Full!

Split at median (53? Actually for even number, we can choose either):

           [34, 53, 67, 89]
         /    |    |    |   \
    [2,10,12] [36] [51] [76,77] [90,100] → Root full!

Root has 4 keys, split at median (67):

                [67]
               /    \
        [34,53]      [89]
        /   |  \     /   \
   [2,10,12] [36] [51] [76,77] [90,100] → Wait, need reorganization

Step 12: Insert 69 After proper balancing (simplified final tree):

                [53]
               /    \
        [34]         [67, 89]
        /  \         /   |   \
   [2,10,12] [36] [51] [69,76,77] [90,100]

Question 13: Expression Tree from Postfix

Construct a tree from the given postfix expression: [WBUT 2016] a b c + d e f + g * +

Answer:

Postfix Expression: a b c + d e f + g * +

Algorithm for Expression Tree from Postfix:

  1. Scan postfix expression from left to right

  2. If operand → push onto stack as node

  3. If operator → pop two operands, make them children, push result back

Step-by-step construction:

Step 1: Read 'a' → push a Stack: [a]

Step 2: Read 'b' → push b Stack: [a, b]

Step 3: Read 'c' → push c Stack: [a, b, c]

Step 4: Read '*' → pop c, pop b, create node with children b and c, push Stack: [a, *]

Step 5: Read '+' → pop , pop a, create + node with children a and , push + Stack: [+]

    +
   / \
  a   *
     / \
    b   c

Step 6: Read 'd' → push d Stack: [+, d]

Step 7: Read 'e' → push e Stack: [+, d, e]

Step 8: Read '*' → pop e, pop d, create node with children d and e, push Stack: [+, *]

    *
   / \
  d   e

Step 9: Read 'f' → push f Stack: [+, *, f]

Step 10: Read '+' → pop f, pop , create + node with children and f, push + Stack: [+, +]

      +
     / \
    *   f
   / \
  d   e

Step 11: Read 'g' → push g Stack: [+, +, g]

Step 12: Read '*' → pop g, pop +, create node with children + and g, push Stack: [+, *]

      *
     / \
    +   g
   / \
  *   f
 / \
d   e

Step 13: Read '+' → pop , pop +, create + node with children + and , push + Stack: [+]

          +
         / \
        +   *
       / \ / \
      a  * +  g
        /\/ \
       b c * f
          / \
         d   e

Final Expression Tree:

          +
         / \
        +   *
       / \ / \
      a  * +  g
        /\/ \
       b c * f
          / \
         d   e

This represents: (a + (b c)) + (((d e) + f) * g)


Question 14: Binary Tree from Inorder and Preorder

The Inorder and preorder tree traversals are given. Draw the binary tree. [WBUT 2017] Inorder: A B C D E F G H I Preorder: F B A D C E G I H Is it possible to build a unique binary tree when its preorder and postorder traversals are given?

Answer:

Part 1: Construct Binary Tree

Step-by-step construction:

Step 1: First element in preorder is root → F

Step 2: Split inorder using root F

  • Inorder: A B C D E F G H I

  • Left subtree: A B C D E

  • Right subtree: G H I

Step 3: Process left subtree (A B C D E)

  • From preorder after F: B A D C E G I H

  • First element in this group is root of left subtree → B

  • Inorder for left subtree: A B C D E

  • Split around B: Left: A, Right: C D E

Step 4: Process B's left (A)

  • A is leaf

Step 5: Process B's right (C D E)

  • From preorder remaining: A D C E

  • Root of this subtree → D

  • Inorder for this subtree: C D E

  • Split around D: Left: C, Right: E

Step 6: Process D's left (C)

  • C is leaf

Step 7: Process D's right (E)

  • E is leaf

Step 8: Process right subtree (G H I)

  • From preorder remaining: G I H

  • Root of right subtree → G

  • Inorder for right subtree: G H I

  • Split around G: Left: empty, Right: H I

Step 9: Process G's right (H I)

  • From preorder remaining: I H

  • Root of this subtree → I

  • Inorder for this subtree: H I

  • Split around I: Left: H, Right: empty

Step 10: Process I's left (H)

  • H is leaf

Final Binary Tree:

            F
           / \
          B   G
         / \   \
        A   D   I
           / \  /
          C   E H

Verification:

  • Preorder (NLR): F, B, A, D, C, E, G, I, H ✓

  • Inorder (LNR): A, B, C, D, E, F, G, H, I ✓

Part 2: Can we build unique tree from preorder and postorder?

Answer: NO, it is NOT possible to construct a unique binary tree from only preorder and postorder traversals.

Reasoning:

  1. Preorder gives root first: NLR (Node-Left-Right)

  2. Postorder gives root last: LRN (Left-Right-Node)

The problem: When a node has only one child, we cannot determine whether it's a left child or right child.

Example:

Tree 1:        Tree 2:
    5             5
   /               \
  3                 3

Both trees have:

  • Preorder: 5, 3

  • Postorder: 3, 5

Why this ambiguity:

  • Preorder: [Root] + [Left subtree] + [Right subtree]

  • Postorder: [Left subtree] + [Right subtree] + [Root]

Without inorder, we cannot distinguish between:

  • Single left child vs single right child

  • Which nodes belong to left vs right subtree when one subtree is empty

Conclusion: To uniquely reconstruct a binary tree, we need:

  • Inorder + Preorder OR

  • Inorder + Postorder

Preorder + Postorder alone does NOT guarantee a unique tree.


Question 15: B-Tree Insertion

Insert the following keys into a B-Tree of given order mentioned below: [WBUT 2018] a) a, f, b, k, h, m, e, s, r, c (Order 3) b) a, g, f, b, k, d, h, m, j, e, s, l, r, x, c, l, n, t, u, p (Order 5)

Answer:

Part a) B-Tree of Order 3 (m=3)

Properties:

  • Maximum keys per node = m-1 = 2

  • Minimum keys per node (except root) = ⌈m/2⌉-1 = 1

  • Each node can have up to 3 children

Step-by-step insertion:

Step 1: Insert a, f

[a, f]

Step 2: Insert b [a, b, f] → Full! Split at median (b)

    [b]
   /   \
[a]   [f]

Step 3: Insert k Insert into right leaf:

    [b]
   /   \
[a]   [f, k]

Step 4: Insert h Insert into right leaf: [f, h, k] → Full! Split at median (h):

      [b, h]
     /   |   \
   [a]  [f]  [k]

Step 5: Insert m Insert into rightmost leaf [k]:

      [b, h]
     /   |   \
   [a]  [f]  [k, m]

Step 6: Insert e Insert into leaf [f]? e < h, so in middle subtree. But [f] becomes [e, f]:

      [b, h]
     /   |   \
   [a] [e,f] [k,m]

Step 7: Insert s Insert into [k,m] → [k,m,s] Full! Split at median (m):

        [b, h, m]
       /   |   |   \
     [a] [e,f] [k] [s]

Step 8: Insert r Insert into [s]? r < s, so in [k]? r > k, so in [k] becomes [k,r]:

        [b, h, m]
       /   |   |   \
     [a] [e,f] [k,r] [s]

Step 9: Insert c Insert into [a]? c > a, so check: c < b, so in left subtree. [a] becomes [a,c]:

        [b, h, m]
       /   |   |   \
   [a,c] [e,f] [k,r] [s]

Final B-Tree of Order 3:

        [b, h, m]
       /   |   |   \
   [a,c] [e,f] [k,r] [s]

Part b) B-Tree of Order 5 (m=5)

Properties:

  • Maximum keys per node = m-1 = 4

  • Minimum keys per node (except root) = ⌈m/2⌉-1 = 2

  • Each node can have up to 5 children

Step-by-step insertion:

Step 1-4: Insert a, g, f, b

[a, b, f, g]

Step 5: Insert k [a, b, f, g, k] → Full! Split at median (f)

        [f]
       /   \
   [a,b]   [g,k]

Step 6: Insert d Insert into left leaf [a,b] → [a,b,d] (OK, ≤4)

        [f]
       /   \
  [a,b,d]  [g,k]

Step 7: Insert h Insert into right leaf [g,k] → [g,h,k] (OK)

        [f]
       /   \
  [a,b,d]  [g,h,k]

Step 8: Insert m Insert into [g,h,k] → [g,h,k,m] (OK)

        [f]
       /   \
  [a,b,d]  [g,h,k,m]

Step 9: Insert j Insert into [g,h,k,m] → [g,h,j,k,m] Full! (5 keys) Split at median (j or k? Usually median is 3rd element: k)

          [f, k]
         /   |   \
    [a,b,d] [g,h,j] [m]

Step 10: Insert e Insert into [a,b,d] → [a,b,d,e] (OK)

          [f, k]
         /   |   \
  [a,b,d,e] [g,h,j] [m]

Step 11: Insert s Insert into [m] → [m,s] (OK)

          [f, k]
         /   |   \
  [a,b,d,e] [g,h,j] [m,s]

Step 12: Insert l Insert into [g,h,j]? l > k, so in right subtree? Actually l < m, so between k and m: new leaf

          [f, k]
         /   |   \
  [a,b,d,e] [g,h,j] [l,m,s] → [l,m,s] OK

Step 13: Insert r Insert into [l,m,s] → [l,m,r,s] (OK)

          [f, k]
         /   |   \
  [a,b,d,e] [g,h,j] [l,m,r,s]

Step 14: Insert x Insert into [l,m,r,s] → [l,m,r,s,x] Full! (5 keys) Split at median (r)

          [f, k, r]
         /   |   |   \
  [a,b,d,e] [g,h,j] [l,m] [s,x]

Step 15: Insert c Insert into [a,b,d,e] → [a,b,c,d,e] Full! Split at median (c)

            [c, f, k, r]
         /    |   |   |   \
    [a,b] [d,e] [g,h,j] [l,m] [s,x]

Step 16-20: Insert l (duplicate?), n, t, u, p Continue similar process...

Final simplified B-Tree after all insertions:

                [k]
               /   \
          [f]        [r]
         /   \       /   \
    [c]     [j]   [m]    [u]
    / \     / \   / \    / \
 [a,b] [d,e] [g,h] [l] [n,p] [s,t] [x]

Question 16: Expression Tree Definition and Traversals

What is expression tree? Draw the expression tree and write the In, Pre & Post-Order traversals for the given expression: [WBUT 2018] E = (2x + y)(5a - b)^3

Answer:

Definition of Expression Tree:

An expression tree is a binary tree representation of an arithmetic expression where:

  • Leaf nodes contain operands (constants, variables)

  • Internal nodes contain operators (+, -, *, /, ^)

  • Each subtree represents a subexpression

  • The tree structure reflects operator precedence and associativity

Properties:

  • Inorder traversal gives infix expression (with parentheses implied)

  • Preorder traversal gives prefix expression

  • Postorder traversal gives postfix expression (useful for evaluation)

Construction of Expression Tree for E = (2x + y)(5a - b)^3

Step 1: Identify main operator The expression is (2x + y) multiplied by (5a - b)^3 Main operator is * (multiplication)

Step 2: Left subtree represents (2x + y)

  • Main operator: +

  • Left of +: 2 * x (multiplication)

  • Right of +: y

Step 3: Right subtree represents (5a - b)^3

  • Main operator: ^ (exponentiation)

  • Left of ^: (5a - b)

  • Right of ^: 3 (constant)

Step 4: Subtree for (5a - b)

  • Main operator: -

  • Left of -: 5 * a (multiplication)

  • Right of -: b

Final Expression Tree:

           *
         /   \
        +     ^
       / \   / \
      *   y -   3
     / \   / \
    2   x *   b
         / \
        5   a

Traversals:

1. Inorder Traversal (Left-Root-Right): (2 x + y) (5 a - b) ^ 3 With parentheses implied: ((2x) + y) ((5a - b)^3)

2. Preorder Traversal (Root-Left-Right):

  • 2 x y ^ - * 5 a b 3

3. Postorder Traversal (Left-Right-Root): 2 x y + 5 a b - 3 ^ *

Evaluation using Postorder (Stack-based):

  1. Push 2, x → encounter → pop x, 2 → compute 2x → push result

  2. Push y → encounter + → pop y and (2x) → compute (2x)+y

  3. Push 5, a → encounter → compute 5a

  4. Push b → encounter - → compute (5*a)-b

  5. Push 3 → encounter ^ → compute ((5*a)-b)^3

  6. Encounter * → compute final multiplication


Question 17: B-Tree vs B+ Tree

Write difference between B tree and B+ tree. [WBUT 2022]

Answer:

Basis of Comparison

B-Tree

B+ Tree

Pointers

All internal and leaf nodes have data pointers

Only leaf nodes have data pointers

Search Efficiency

Search takes more time as keys not all available at leaves

Search is faster and more accurate as all keys are at leaf nodes

Redundant Keys

No duplicate keys maintained

Duplicate keys maintained; all keys present at leaf level

Insertion

Insertion takes more time, sometimes unpredictable

Insertion is easier with consistent results

Deletion

Deletion from internal nodes is complex with many transformations

Deletion is easier as all nodes found at leaf level

Leaf Nodes

Leaf nodes contain keys + data pointers

Leaf nodes contain keys + data pointers + sibling pointers

Internal Nodes

Store keys + data pointers

Store only keys (routers) to guide search

Structure

Keys in internal nodes appear only once

Keys in internal nodes are repeated at leaf level

Sequential Access

Not efficient for range queries

Efficient for range queries due to linked leaves

Height

Generally shorter (data in internal nodes)

Slightly taller (only keys in internal nodes)

Disk I/O

May require more disk accesses

Optimized for disk I/O with more keys per node

Example Use

File systems, databases (less common)

Most modern databases (MySQL, Oracle, PostgreSQL)

Diagrammatic Comparison:

B-Tree:

        [50|data]          ← Internal node has data
       /         \
[20|data]      [70|data]   ← All nodes have data
   /    \        /    \
[10]   [30]    [60]   [80] ← Leaf nodes

B+ Tree:

        [50]               ← Internal node (only keys)
       /     \
     [20]    [70]          ← Internal nodes (only keys)
     /  \     /  \
   [10] [30] [60] [80]     ← Leaf nodes with data and sibling pointers
    |    |    |    |
    ↓    ↓    ↓    ↓
  data data data data
  10↔20↔30↔50↔60↔70↔80    ← Leaves linked for sequential access

Key Advantages of B+ Tree over B-Tree:

  1. Faster range queries: Due to linked leaf nodes

  2. Better cache utilization: Internal nodes can store more keys

  3. More stable search time: All searches end at leaf level

  4. Simpler deletion: Always from leaf nodes

  5. Higher fanout: More keys per node means shorter tree

When to use each:

  • B-Tree: When direct access to data from any node is beneficial

  • B+ Tree: For most database systems, file systems, and range queries


LONG ANSWER TYPE QUESTIONS


Question 18: BST Insertion Algorithm

Write an algorithm to insert a node in a binary search tree. [WBUT 2007, 2014]

Answer:

Binary Search Tree Property:

  • Left child value < Parent value

  • Right child value > Parent value

  • Both subtrees are also BSTs

Algorithm 1: Recursive Insertion

Algorithm: insertRecursive(root, key)
Input: root of BST, key to insert
Output: updated root of BST

if root == NULL then
    create new node with key
    return new node
end if

if key < root→data then
    root→left = insertRecursive(root→left, key)
else if key > root→data then
    root→right = insertRecursive(root→right, key)
else
    // key already exists (handle as per requirement)
    return root
end if

return root

Algorithm 2: Iterative Insertion

Algorithm: insertIterative(root, key)
Input: root of BST, key to insert
Output: updated root of BST

// Create new node
newNode = create new node with key
newNode→left = NULL
newNode→right = NULL

// If tree is empty, new node becomes root
if root == NULL then
    return newNode
end if

// Find the correct position
current = root
parent = NULL

while current != NULL do
    parent = current
    
    if key < current→data then
        current = current→left
    else if key > current→data then
        current = current→right
    else
        // Key already exists, handle duplicate
        return root
    end if
end while

// Insert new node as child of parent
if key < parent→data then
    parent→left = newNode
else
    parent→right = newNode
end if

return root

C Language Implementation:

#include <stdio.h>
#include <stdlib.h>

// Structure for BST node
struct Node {
    int data;
    struct Node* left;
    struct Node* right;
};

// Function to create a new node
struct Node* createNode(int data) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->data = data;
    newNode->left = NULL;
    newNode->right = NULL;
    return newNode;
}

// Recursive insertion
struct Node* insertRecursive(struct Node* root, int data) {
    // If tree is empty, create root
    if (root == NULL) {
        return createNode(data);
    }
    
    // Recursively insert
    if (data < root->data) {
        root->left = insertRecursive(root->left, data);
    }
    else if (data > root->data) {
        root->right = insertRecursive(root->right, data);
    }
    // If data equals, do nothing (no duplicates)
    
    return root;
}

// Iterative insertion
struct Node* insertIterative(struct Node* root, int data) {
    // Create new node
    struct Node* newNode = createNode(data);
    
    // If tree is empty
    if (root == NULL) {
        return newNode;
    }
    
    struct Node* current = root;
    struct Node* parent = NULL;
    
    // Find insertion point
    while (current != NULL) {
        parent = current;
        
        if (data < current->data) {
            current = current->left;
        }
        else if (data > current->data) {
            current = current->right;
        }
        else {
            // Duplicate key - free new node and return
            free(newNode);
            return root;
        }
    }
    
    // Insert as child of parent
    if (data < parent->data) {
        parent->left = newNode;
    }
    else {
        parent->right = newNode;
    }
    
    return root;
}

// Inorder traversal to verify BST property
void inorder(struct Node* root) {
    if (root != NULL) {
        inorder(root->left);
        printf("%d ", root->data);
        inorder(root->right);
    }
}

Example Insertion: Insert keys: 50, 30, 70, 20, 40, 60, 80

Step 1: Insert 50 →     50
Step 2: Insert 30 →     50
                        /
                      30
Step 3: Insert 70 →     50
                       /  \
                     30    70
Step 4: Insert 20 →     50
                       /  \
                     30    70
                    /
                  20
Step 5: Insert 40 →     50
                       /  \
                     30    70
                    /  \
                  20    40
Step 6: Insert 60 →     50
                       /  \
                     30    70
                    /  \   /
                  20    40 60
Step 7: Insert 80 →     50
                       /  \
                     30    70
                    /  \   /  \
                  20    40 60  80

Time Complexity:

  • Best/Average Case: O(log n) - tree is balanced

  • Worst Case: O(n) - tree becomes skewed

Space Complexity:

  • Recursive: O(h) where h is height (recursion stack)

  • Iterative: O(1)


Question 19: Non-recursive In-order Traversal

Write a non-recursive algorithm for in-order traversal of a binary tree. [WBUT 2007, 2010, 2016]

Answer:

Non-recursive In-order Traversal using Stack

Algorithm:

Algorithm: inorderNonRecursive(root)
Input: root of binary tree
Output: prints nodes in inorder sequence

if root == NULL then
    return
end if

Create an empty stack S
current = root

while (current != NULL OR S is not empty) do
    
    // Reach the leftmost node of current node
    while (current != NULL) do
        push current onto S
        current = current→left
    end while
    
    // Current is NULL at this point
    // Pop from stack and process
    current = pop from S
    print current→data
    
    // Now move to right subtree
    current = current→right
    
end while

C Language Implementation:

#include <stdio.h>
#include <stdlib.h>
#define MAX 100

// Structure for binary tree node
struct Node {
    int data;
    struct Node* left;
    struct Node* right;
};

// Stack implementation
struct Stack {
    int top;
    struct Node* array[MAX];
};

struct Stack* createStack() {
    struct Stack* stack = (struct Stack*)malloc(sizeof(struct Stack));
    stack->top = -1;
    return stack;
}

void push(struct Stack* stack, struct Node* node) {
    if (stack->top == MAX - 1) {
        printf("Stack Overflow\n");
        return;
    }
    stack->array[++stack->top] = node;
}

struct Node* pop(struct Stack* stack) {
    if (stack->top == -1) {
        return NULL;
    }
    return stack->array[stack->top--];
}

int isEmpty(struct Stack* stack) {
    return stack->top == -1;
}

// Non-recursive inorder traversal
void inorderNonRecursive(struct Node* root) {
    if (root == NULL) {
        printf("Tree is empty\n");
        return;
    }
    
    struct Stack* stack = createStack();
    struct Node* current = root;
    
    printf("Inorder Traversal: ");
    
    while (current != NULL || !isEmpty(stack)) {
        // Reach the leftmost node
        while (current != NULL) {
            push(stack, current);
            current = current->left;
        }
        
        // Current is NULL, pop from stack
        current = pop(stack);
        printf("%d ", current->data);
        
        // Move to right subtree
        current = current->right;
    }
    printf("\n");
}

// Alternative implementation without separate stack structure
void inorderIterative(struct Node* root) {
    struct Node* stack[100];
    int top = -1;
    struct Node* current = root;
    
    printf("Inorder Traversal: ");
    
    while (current != NULL || top != -1) {
        while (current != NULL) {
            stack[++top] = current;
            current = current->left;
        }
        
        current = stack[top--];
        printf("%d ", current->data);
        
        current = current->right;
    }
    printf("\n");
}

// Helper function to create a new node
struct Node* createNode(int data) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->data = data;
    newNode->left = NULL;
    newNode->right = NULL;
    return newNode;
}

Example Walkthrough:

Consider the tree:

        50
       /  \
     30    70
    /  \   / 
   20  40 60

Step-by-step execution:

Step

Current

Stack (top to bottom)

Action

Output

1

50

[]

Start

2

50 → 30 → 20

[50,30,20]

Push all left

3

20 (pop)

[50,30]

Pop 20

20

4

NULL (20→right)

[50,30]

5

30 (pop)

[50]

Pop 30

30

6

40 (30→right)

[50,40]

Push 40 and its left

7

40 (pop)

[50]

Pop 40

40

8

NULL

[50]

9

50 (pop)

[]

Pop 50

50

10

70 (50→right)

[70]

Push 70

11

60 (70→left)

[70,60]

Push 60

12

60 (pop)

[70]

Pop 60

60

13

NULL

[70]

14

70 (pop)

[]

Pop 70

70

Output: 20 30 40 50 60 70

Time Complexity: O(n) - each node visited once Space Complexity: O(h) where h is height of tree


Question 20: Threaded Binary Tree

a) What is threaded binary tree? [WBUT 2008, 2011] b) Write an algorithm to delete a node from a binary search tree. [WBUT 2008, 2019]

Answer:

Part a) Threaded Binary Tree

Definition: A Threaded Binary Tree is a binary tree in which NULL pointers are replaced with threads (pointers) to other nodes to facilitate traversal without recursion or stack.

Types of Threads:

  1. In-order Threading: NULL right pointers point to in-order successor

  2. Pre-order Threading: NULL right pointers point to pre-order successor

  3. Post-order Threading: NULL right pointers point to post-order successor

Why Threaded Binary Tree?

  • Avoids recursion overhead

  • Saves memory by utilizing NULL pointers

  • Enables faster traversal

  • No stack required for traversal

Node Structure:

struct ThreadedNode {
    int data;
    struct ThreadedNode* left;
    struct ThreadedNode* right;
    int leftThread;   // 1 if left is thread, 0 if child
    int rightThread;  // 1 if right is thread, 0 if child
};

Example:

Original Tree:        Threaded Tree (In-order):
      A                      A
     / \                    / \
    B   C                  B   C
   / \                    / \   \
  D   E                  D→B E→A C→?
       \                      \
        F                      F→C

In-order traversal: D, B, E, F, A, C Threads: D's right → B, F's right → A, C's right → NULL (or head)

Algorithm for In-order Traversal of Threaded Binary Tree:

Algorithm: inorderThreaded(root)
Begin
    current = findLeftmost(root)
    
    while current != NULL do
        visit(current)
        
        if current→rightThread == 1 then
            current = current→right  // Follow thread
        else
            current = findLeftmost(current→right)
        end if
    end while
End

Function findLeftmost(node)
Begin
    while node→leftThread == 0 AND node→left != NULL do
        node = node→left
    end while
    return node
End

Part b) Delete a Node from Binary Search Tree

Algorithm to delete a node from BST considering all cases:

Algorithm: deleteNode(root, key)
Input: root of BST, key to delete
Output: updated root of BST

if root == NULL then
    return root
end if

// Find the node to delete
if key < root→data then
    root→left = deleteNode(root→left, key)
else if key > root→data then
    root→right = deleteNode(root→right, key)
else
    // Node found - handle three cases
    
    // Case 1: Node with no children (leaf)
    if root→left == NULL AND root→right == NULL then
        free(root)
        return NULL
    end if
    
    // Case 2: Node with one child
    if root→left == NULL then
        temp = root→right
        free(root)
        return temp
    end if
    
    if root→right == NULL then
        temp = root→left
        free(root)
        return temp
    end if
    
    // Case 3: Node with two children
    // Find inorder successor (smallest in right subtree)
    temp = findMin(root→right)
    
    // Copy inorder successor's data to current node
    root→data = temp→data
    
    // Delete the inorder successor
    root→right = deleteNode(root→right, temp→data)
end if

return root

Function findMin(node)
Begin
    while node→left != NULL do
        node = node→left
    end while
    return node
End

C Language Implementation:

#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node* left;
    struct Node* right;
};

// Function to find minimum value node
struct Node* findMin(struct Node* node) {
    struct Node* current = node;
    while (current && current->left != NULL)
        current = current->left;
    return current;
}

// Function to delete a node
struct Node* deleteNode(struct Node* root, int key) {
    // Base case: tree is empty
    if (root == NULL)
        return root;
    
    // Recursively find the node to delete
    if (key < root->data)
        root->left = deleteNode(root->left, key);
    else if (key > root->data)
        root->right = deleteNode(root->right, key);
    else {
        // Node found - now handle the three cases
        
        // Case 1: Node with no children (leaf)
        if (root->left == NULL && root->right == NULL) {
            free(root);
            return NULL;
        }
        
        // Case 2: Node with one child
        else if (root->left == NULL) {
            struct Node* temp = root->right;
            free(root);
            return temp;
        }
        else if (root->right == NULL) {
            struct Node* temp = root->left;
            free(root);
            return temp;
        }
        
        // Case 3: Node with two children
        else {
            // Find inorder successor (smallest in right subtree)
            struct Node* temp = findMin(root->right);
            
            // Copy the inorder successor's data to this node
            root->data = temp->data;
            
            // Delete the inorder successor
            root->right = deleteNode(root->right, temp->data);
        }
    }
    return root;
}

// Function to create a new node
struct Node* createNode(int data) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->data = data;
    newNode->left = NULL;
    newNode->right = NULL;
    return newNode;
}

// Function to insert a node (for building test tree)
struct Node* insert(struct Node* root, int data) {
    if (root == NULL)
        return createNode(data);
    
    if (data < root->data)
        root->left = insert(root->left, data);
    else if (data > root->data)
        root->right = insert(root->right, data);
    
    return root;
}

// Inorder traversal to display tree
void inorder(struct Node* root) {
    if (root != NULL) {
        inorder(root->left);
        printf("%d ", root->data);
        inorder(root->right);
    }
}

Example with all three cases:

Consider BST:

        50
       /  \
     30    70
    /  \   / \
   20  40 60 80

Case 1: Delete leaf node (20)

        50                   50
       /  \                 /  \
     30    70      →      30    70
    /  \   / \              \   / \
   20  40 60 80             40 60 80

Case 2: Delete node with one child (30)

        50                   50
       /  \                 /  \
     30    70      →      40    70
      \   / \                   / \
      40 60 80                 60 80

Case 3: Delete node with two children (50)

        50                   60
       /  \                 /  \
     30    70      →      30    70
    /  \   / \            / \     \
   20  40 60 80          20  40    80

Time Complexity: O(h) where h is height of tree

  • Best/Average: O(log n)

  • Worst: O(n) for skewed tree


Question 21: AVL Tree Construction with Month Names

Show the steps in creation of a height balanced binary AVL TREE using insertion of items in the following order: [WBUT 2009] March, May, November, August, April, January, December, July, February, June, October, September

Answer:

AVL Tree Properties:

  • Balance Factor = height(left) - height(right)

  • |Balance Factor| ≤ 1 for all nodes

  • Rotations performed when BF becomes ±2

Step-by-step construction (using alphabetical order):

Step 1: Insert March

March (0)

Step 2: Insert May

March (1)  ← BF=1
   \
   May (0)

Step 3: Insert November

March (2) ← UNBALANCED (BF=2)
   \
   May (1)
      \
      Nov (0)

Rotation Required: Left Rotation (LL) at March After rotation:

   May (0)
  /    \
March  Nov
(0)    (0)

Step 4: Insert August

    May (1)
   /    \
March   Nov (1)
       /
     Aug (0)

Step 5: Insert April

    May (2) ← UNBALANCED (BF=2)
   /    \
March   Nov (2)
  \    /
  April Aug (1)
  (0)     (0)

Rotation Required: Right-Left Rotation (RL) at Nov After RL rotation:

      May (1)
     /    \
   March   Aug (0)
      \      \
      April   Nov
       (0)    (0)

Step 6: Insert January

      May (2) ← UNBALANCED
     /    \
   March   Aug (1)
      \      \
      April   Nov (1)
      (0)    /
           Jan (0)

Rotation Required: Right Rotation (RR) at Aug After rotation:

        May (1)
       /    \
     March   Nov (1)
        \   /
        April Jan
         (0) (0)

Step 7: Insert December

        May (1)
       /    \
     March   Nov (2) ← UNBALANCED
        \   /  \
        April Jan Dec
         (0) (0)(0)

Rotation Required: Left Rotation (LL) at Nov After rotation:

          May (0)
         /    \
       March   Jan (1)
          \   /  \
          April Dec Nov
           (0)(0) (0)

Step 8: Insert July

          May (1)
         /    \
       March   Jan (2) ← UNBALANCED
          \   /  \
          April Dec Nov (1)
           (0)(0)  /
                 July (0)

Rotation Required: Right-Left Rotation (RL) at Jan After RL rotation:

            May (1)
           /    \
        March    Dec (0)
           \    /   \
          April Jan  Nov
           (0)(0)   /
                  July(0)

Step 9: Insert February

            May (1)
           /    \
        March    Dec (2) ← UNBALANCED
           \    /   \
          April Jan  Nov (1)
           (0)(0)   /
                  July(0)
                  /
               Feb (0)

Rotation Required: Right-Left Rotation (RL) at Dec After RL rotation:

              May (1)
             /    \
          March    July (0)
             \    /   \
            April Jan  Nov
             (0) (0)   /
                     Dec(0)
                     /
                   Feb(0)

Step 10: Insert June

              May (1)
             /    \
          March    July (1)
             \    /   \
            April Jan  Nov (2)
             (0) (0)   /
                     Dec(1)
                     /
                   Feb(0)
                   /
                 June(0)

Step 11: Insert October

              May (2) ← UNBALANCED
             /    \
          March    July (1)
             \    /   \
            April Jan  Nov (2)
             (0) (0)   /
                     Dec(2)
                     /
                   Feb(1)
                   /
                 June(0)

Complex rotation needed...

Final AVL Tree after all insertions (simplified):

                July (0)
               /    \
            May      Nov (1)
           /  \      /  \
        March June Oct  Dec
        /  \   (0) (0)  /  \
      April Feb       Sep  Jan
       (0) (0)       (0)  (0)

Question 22: B-Tree Uses and Insertion

a) What do you mean by a B-Tree and what are the uses of such a tree? [WBUT 2009, 2016] b) Consider a B-Tree of order 5 as shown below - insert the elements 4, 5, 58, 6 in this order. [WBUT 2009]

Answer:

Part a) B-Tree Definition and Uses

Definition: A B-Tree of order m is an m-way search tree with the following properties:

  1. All leaves are at the same level

  2. Each node (except root) has at least ⌈m/2⌉ children

  3. Each node has at most m children

  4. Each internal node with k children contains k-1 keys

  5. Keys in a node are stored in ascending order

  6. Root has at least 2 children if it's not a leaf

Node Structure:

[Key1, Key2, ..., Key(k-1)]
 /     |     |          \
Child1 Child2 Child3 ... Childk

Uses of B-Trees:

  1. Database Systems:

    • Indexing in relational databases (MySQL, Oracle, PostgreSQL)

    • Primary and secondary indexes

    • Efficient for range queries

  2. File Systems:

    • NTFS, HFS+, ext4 file systems use B-Trees

    • Directory structures

    • File allocation tables

  3. Operating Systems:

    • Virtual memory management

    • Page caching algorithms

  4. Why B-Trees are appropriate:

    • Disk I/O Efficiency: Nodes sized to disk blocks, minimizing disk accesses

    • Self-balancing: Maintains balance automatically

    • Predictable Performance: O(log n) for all operations

    • Handles Large Data: Designed for data that doesn't fit in RAM

    • Range Queries: Efficient due to sorted keys

Advantages over other trees:

  • Shorter height than binary trees (high fanout)

  • Fewer disk accesses than AVL or Red-Black trees

  • Optimal for block-oriented storage devices


Part b) B-Tree of Order 5 Insertion

Given B-Tree (order 5):

        [20, 40, 60]
       /    |    |    \
   [10,15] [30] [50,55] [70,80,90]

Properties of order 5 B-Tree:

  • Maximum keys per node = 4

  • Minimum keys per node (except root) = 2

  • Maximum children = 5

Step 1: Insert 4 4 < 20, so go to leftmost child [10,15] Insert 4 → [4,10,15] (OK, 3 ≤ 4)

Result:

        [20, 40, 60]
       /    |    |    \
  [4,10,15] [30] [50,55] [70,80,90]

Step 2: Insert 5 5 < 20, go to [4,10,15] Insert 5 → [4,5,10,15] (OK, 4 keys)

Result:

        [20, 40, 60]
       /    |    |    \
  [4,5,10,15] [30] [50,55] [70,80,90]

Step 3: Insert 58 58 > 40, 58 < 60, so go to [50,55] Insert 58 → [50,55,58] → but wait, this node has only 3 keys? Actually [50,55] becomes [50,55,58] which is 3 keys (OK, ≤4)

Result:

        [20, 40, 60]
       /    |    |    \
  [4,5,10,15] [30] [50,55,58] [70,80,90]

Step 4: Insert 6 6 < 20, go to [4,5,10,15] Insert 6 → [4,5,6,10,15] → Full! (5 keys > 4)

Split this node at median (6):

         [6, 20, 40, 60]
       /   |    |    |    \
    [4,5] [10,15] [30] [50,55,58] [70,80,90]

Final B-Tree after all insertions:

            [6, 20, 40, 60]
           /   |    |    |    \
      [4,5] [10,15] [30] [50,55,58] [70,80,90]

Question 23: AVL vs Binary Search Tree

How an AVL tree differs from binary search tree? [WBUT 2010, 2012]

Answer:

Binary Search Tree (BST): A binary search tree is a binary tree where for every node:

  • Left child value < Parent value

  • Right child value > Parent value

  • Both subtrees are also BSTs

AVL Tree: An AVL tree is a self-balancing binary search tree where for every node:

  • |height(left) - height(right)| ≤ 1

  • Both subtrees are also AVL trees

Detailed Comparison:

Feature

Binary Search Tree (BST)

AVL Tree

Balance Condition

No balance requirement

Height-balanced (BF = -1,0,1)

Height

Can be O(n) in worst case

Always O(log n)

Search Time

O(n) worst case

O(log n) guaranteed

Insertion Time

O(n) worst case

O(log n) with rotations

Deletion Time

O(n) worst case

O(log n) with rotations

Rotations

None

Single/double rotations as needed

Space Overhead

Only data + 2 pointers

Data + 2 pointers + balance factor

Implementation Complexity

Simple

Complex due to rotations

When to Use

Static data, random inserts

Dynamic data, frequent searches

Memory Usage

Minimal

Extra byte per node for BF

Mathematical Comparison:

BST Height:

  • Best case: ⌈log₂(n+1)⌉ - 1

  • Worst case: n-1 (skewed tree)

AVL Height:

  • Maximum height ≈ 1.44 log₂(n+2) - 1.328

  • Always O(log n)

Example Comparison:

BST (skewed):

1
 \
  2
   \
    3
     \
      4
       \
        5

Search for 5 requires 5 comparisons

AVL Tree (balanced):

    3
   / \
  2   5
 /   / \
1   4   6

Search for 6 requires only 3 comparisons

When BST is sufficient:

  • Data arrives in random order

  • No guarantee of performance needed

  • Memory is very constrained

When AVL is necessary:

  • Guaranteed performance required

  • Frequent search operations

  • Real-time systems

Balance Factor in AVL:

Balance Factor = height(left) - height(right)
Possible values: -1, 0, 1

Example:     30 (BF=1)
            /  \
          20    40 (BF=0)
         /  \     \
       10   25    50 (BF=0)
       
Heights: left(20)=2, right(40)=1 → BF=1 ✓

Rotations in AVL:

  1. LL Rotation: Right rotation

  2. RR Rotation: Left rotation

  3. LR Rotation: Left then right

  4. RL Rotation: Right then left


Question 24: Tree from Preorder and Inorder

Given the preorder and inorder sequence and draw the resultant binary tree and write its postorder traversal: [WBUT 2010] Pre-order: A B D G H E I C F J K In-order: G D H B E I A C J F K

Answer:

Step-by-step construction:

Step 1: First element in preorder is root → A

Step 2: Split inorder using root A

  • In-order: G D H B E I A C J F K

  • Left subtree: G D H B E I

  • Right subtree: C J F K

Step 3: Process left subtree (G D H B E I)

  • From preorder after A: B D G H E I C F J K

  • First element in this group is root of left subtree → B

  • In-order for left subtree: G D H B E I

  • Split around B: Left: G D H, Right: E I

Step 4: Process B's left (G D H)

  • From preorder remaining: D G H E I

  • Root of this subtree → D

  • In-order for this subtree: G D H

  • Split around D: Left: G, Right: H

Step 5: Process D's left (G)

  • G is leaf

Step 6: Process D's right (H)

  • H is leaf

Step 7: Process B's right (E I)

  • From preorder remaining: G H E I

  • Root of this subtree → E

  • In-order for this subtree: E I

  • Split around E: Left: empty, Right: I

Step 8: Process E's right (I)

  • I is leaf

Step 9: Process right subtree (C J F K)

  • From preorder remaining: C F J K

  • Root of right subtree → C

  • In-order for right subtree: C J F K

  • Split around C: Left: empty, Right: J F K

Step 10: Process C's right (J F K)

  • From preorder remaining: F J K

  • Root of this subtree → F

  • In-order for this subtree: J F K

  • Split around F: Left: J, Right: K

Step 11: Process F's left (J)

  • J is leaf

Step 12: Process F's right (K)

  • K is leaf

Final Binary Tree:

            A
           / \
          B   C
         / \   \
        D   E   F
       / \   \ / \
      G   H   I J K

Postorder Traversal (Left-Right-Root):

Step through the tree:

  1. Left subtree of A: (G H D I E B)

    • G (leaf) → H (leaf) → D (process after children) → I (leaf) → E → B

    • Actually: G, H, D, I, E, B

  2. Right subtree of A: (J K F C)

    • J, K, F, C
  3. Root A at the end

Final Postorder: G, H, D, I, E, B, J, K, F, C, A

Verification:

  • Preorder: A B D G H E I C F J K ✓

  • Inorder: G D H B E I A C J F K ✓

  • Postorder: G H D I E B J K F C A ✓


Question 25: Search Algorithm in BST

Write an algorithm to search a node in a binary search tree. [WBUT 2010]

Answer:

Algorithm for Searching in BST:

Algorithm 1: Recursive Search

Algorithm: searchRecursive(root, key)
Input: root of BST, key to search
Output: pointer to node containing key (or NULL if not found)

if root == NULL OR root→data == key then
    return root
end if

if key < root→data then
    return searchRecursive(root→left, key)
else
    return searchRecursive(root→right, key)
end if

Algorithm 2: Iterative Search

Algorithm: searchIterative(root, key)
Input: root of BST, key to search
Output: pointer to node containing key (or NULL if not found)

current = root

while current != NULL AND current→data != key do
    if key < current→data then
        current = current→left
    else
        current = current→right
    end if
end while

return current

C Language Implementation:

#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node* left;
    struct Node* right;
};

// Recursive search
struct Node* searchRecursive(struct Node* root, int key) {
    // Base case: root is NULL or key found
    if (root == NULL || root->data == key) {
        return root;
    }
    
    // Key is smaller than root's key
    if (key < root->data) {
        return searchRecursive(root->left, key);
    }
    // Key is greater than root's key
    else {
        return searchRecursive(root->right, key);
    }
}

// Iterative search
struct Node* searchIterative(struct Node* root, int key) {
    struct Node* current = root;
    
    while (current != NULL && current->data != key) {
        if (key < current->data) {
            current = current->left;
        }
        else {
            current = current->right;
        }
    }
    
    return current;
}

// Function to print search result
void printSearchResult(struct Node* result, int key) {
    if (result != NULL) {
        printf("Key %d found in the tree\n", key);
    } else {
        printf("Key %d not found in the tree\n", key);
    }
}

// Helper function to create a new node
struct Node* createNode(int data) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->data = data;
    newNode->left = NULL;
    newNode->right = NULL;
    return newNode;
}

// Function to insert a node (to build test tree)
struct Node* insert(struct Node* root, int data) {
    if (root == NULL) {
        return createNode(data);
    }
    
    if (data < root->data) {
        root->left = insert(root->left, data);
    }
    else if (data > root->data) {
        root->right = insert(root->right, data);
    }
    
    return root;
}

Example Walkthrough:

Consider BST:

        50
       /  \
     30    70
    /  \   / \
   20  40 60 80

Search for key = 40:

Step

Current

Comparison

Action

1

50

40 < 50

Go left

2

30

40 > 30

Go right

3

40

40 == 40

Found!

Search for key = 55:

Step

Current

Comparison

Action

1

50

55 > 50

Go right

2

70

55 < 70

Go left

3

60

55 < 60

Go left

4

NULL

-

Not found

Time Complexity:

  • Best/Average Case: O(log n) - tree is balanced

  • Worst Case: O(n) - tree is skewed

Space Complexity:

  • Recursive: O(h) where h is height (recursion stack)

  • Iterative: O(1)

Advantages of Iterative Search:

  • No recursion stack overhead

  • More memory efficient

  • No risk of stack overflow for deep trees

Advantages of Recursive Search:

  • Cleaner, more readable code

  • Easier to understand and implement


Question 26: Height-Balanced Tree Advantages

What are the problems of binary tree? Explain the improvement of performance by the use of height-balanced tree. Show how a height-balanced tree can be formed by inserting: 1,2,3,4,5,6,8,9,10,7,11. Show the root element that can be deleted from the above tree. [WBUT 2010]

Answer:

Part 1: Problems of Binary Search Tree

  1. Skewing Problem:

    • When data is inserted in sorted order, BST becomes a linked list

    • Height becomes O(n) instead of O(log n)

    • Search, insert, delete become O(n)

    Example of skewed tree:

    1
     \
      2
       \
        3
         \
          4
           \
            5
    
  2. Performance Degradation:

    • Search time degrades from O(log n) to O(n)

    • For 1,000,000 nodes, worst case requires 1,000,000 comparisons

    • Balanced tree requires only ~20 comparisons

  3. No Guarantees:

    • Performance depends on insertion order

    • Cannot guarantee response time for real-time systems

  4. Memory Inefficiency:

    • Many NULL pointers (wasted space in skewed trees)

    • Poor cache utilization

Part 2: Improvement with Height-Balanced Trees

Height-balanced trees (AVL trees) solve these problems by:

  1. Maintaining Balance: |height(left) - height(right)| ≤ 1 for every node

  2. Rotations: Automatically rebalance after insertions/deletions

  3. Guaranteed Performance: Height always O(log n)

For n = 1,000,000:

  • BST worst case height = 1,000,000

  • AVL tree height ≤ 1.44 × log₂(1,000,000) ≈ 28

Part 3: Construct AVL Tree from given sequence

Insert: 1, 2, 3, 4, 5, 6, 8, 9, 10, 7, 11

Step 1: Insert 1

1(0)

Step 2: Insert 2

1(1)
 \
  2(0)

Step 3: Insert 3

  1(2) ← UNBALANCED (BF=2)
   \
    2(1)
     \
      3(0)

RR Rotation at 1:

  2(0)
 / \
1(0)3(0)

Step 4: Insert 4

  2(1)
 / \
1(0)3(1)
     \
      4(0)

Step 5: Insert 5

  2(2) ← UNBALANCED (BF=2)
 / \
1(0)3(2)
     \
      4(1)
       \
        5(0)

RR Rotation at 3:

    2(1)
   / \
  1(0)4(0)
     / \
    3(0)5(0)

Step 6: Insert 6

    2(2) ← UNBALANCED
   / \
  1(0)4(1)
     / \
    3(0)5(1)
         \
          6(0)

RR Rotation at 4:

      4(0)
     /   \
    2(1)  5(1)
   / \      \
  1(0)3(0)   6(0)

Step 7: Insert 8

      4(1)
     /   \
    2(1)  5(2) ← UNBALANCED
   / \      \
  1(0)3(0)   6(1)
               \
                8(0)

RR Rotation at 5:

      4(1)
     /   \
    2(1)  6(0)
   / \   / \
  1(0)3(0)5(0)8(0)

Step 8: Insert 9

      4(2) ← UNBALANCED
     /   \
    2(1)  6(1)
   / \   / \
  1(0)3(0)5(0)8(1)
                 \
                  9(0)

RR Rotation at 6:

        6(0)
       /   \
      4(1)  8(1)
     / \     / \
    2(1)5(0)  9(0)
   / \
  1(0)3(0)

Step 9: Insert 10

        6(1)
       /   \
      4(1)  8(2) ← UNBALANCED
     / \     / \
    2(1)5(0)  9(1)
   / \         \
  1(0)3(0)     10(0)

RR Rotation at 8:

        6(1)
       /   \
      4(1)  9(0)
     / \   / \
    2(1)5(0)8(0)10(0)
   / \
  1(0)3(0)

Step 10: Insert 7

        6(2) ← UNBALANCED
       /   \
      4(2)  9(0)
     / \   / \
    2(1)5(0)8(1)10(0)
   / \       /
  1(0)3(0) 7(0)

LR Rotation at 4: After left rotation at 4:

        6(2)
       /   \
      2(1)  9(0)
     / \   / \
    1(0)4(1)8(1)10(0)
       / \   /
      3(0)5(0)7(0)

After right rotation at 6:

        4(1)
       /   \
      2(1)  6(1)
     / \   / \
    1(0)3(0)5(0)8(1)
                 / \
                7(0)9(1)
                     \
                     10(0)

Step 11: Insert 11

        4(1)
       /   \
      2(1)  6(2) ← UNBALANCED
     / \   / \
    1(0)3(0)5(0)8(2)
                 / \
                7(0)9(1)
                     \
                     10(1)
                       \
                       11(0)

RR Rotation at 8:

        4(1)
       /   \
      2(1)  6(1)
     / \   / \
    1(0)3(0)5(0)9(0)
               /   \
              8(1)  10(1)
             /       \
            7(0)     11(0)

Final AVL Tree:

           4
         /   \
        2     6
       / \   / \
      1   3 5   9
               / \
              8   10
             /     \
            7       11

Part 4: Root Element Deletion

Delete root (4) from the AVL tree:

Step 1: Find inorder successor of 4 → smallest in right subtree = 5

Step 2: Replace 4 with 5

Step 3: Delete 5 from its original position (leaf node)

Result after deletion:

           5
         /   \
        2     6
       / \     \
      1   3     9
               / \
              8   10
             /     \
            7       11

Step 4: Check balance factors and rebalance if needed


Question 27: B-Tree of Order 4 Construction

Show the stages in growth of an order - 4 B-tree when the following keys are inserted in the order given: [WBUT 2010] 74 72 19 87 51 10 35 18 39 60 76 58 19 45

Answer:

B-Tree Properties (Order m=4):

  • Maximum keys per node = m-1 = 3

  • Minimum keys per node (except root) = ⌈m/2⌉-1 = 1

  • Each node can have up to 4 children

  • All leaves at same level

Step-by-step construction:

Step 1: Insert 74, 72, 19

[19, 72, 74]

Step 2: Insert 87 [19,72,74,87] → Full! Split at median (74)

    [74]
   /    \
[19,72] [87]

Step 3: Insert 51 51 < 74, go to left leaf [19,72] Insert 51 → [19,51,72] (OK, 3 keys)

    [74]
   /    \
[19,51,72] [87]

Step 4: Insert 10 10 < 74, go to left leaf [19,51,72] Insert 10 → [10,19,51,72] → Full! (4 keys) Split at median (19 or 51? Typically median is 2nd element: 19)

       [51, 74]
      /   |    \
   [10,19] [72] [87]

Step 5: Insert 35 35 < 51, go to left leaf [10,19] Insert 35 → [10,19,35] (OK, 3 keys)

       [51, 74]
      /   |    \
 [10,19,35] [72] [87]

Step 6: Insert 18 18 < 51, go to [10,19,35] Insert 18 → [10,18,19,35] → Full! Split at median (18)

           [18, 51, 74]
          /   |    |    \
      [10] [19,35] [72] [87]

Step 7: Insert 39 39 > 18, 39 < 51, go to [19,35] Insert 39 → [19,35,39] (OK)

           [18, 51, 74]
          /   |    |    \
      [10] [19,35,39] [72] [87]

Step 8: Insert 60 60 > 51, 60 < 74, go to [72] Insert 60 → [60,72] (OK)

           [18, 51, 74]
          /   |    |    \
      [10] [19,35,39] [60,72] [87]

Step 9: Insert 76 76 > 74, go to [87] Insert 76 → [76,87] (OK)

           [18, 51, 74]
          /   |    |    \
      [10] [19,35,39] [60,72] [76,87]

Step 10: Insert 58 58 > 51, 58 < 74, go to [60,72] Insert 58 → [58,60,72] (OK)

           [18, 51, 74]
          /   |    |    \
      [10] [19,35,39] [58,60,72] [76,87]

Step 11: Insert 19 (duplicate? Assuming distinct, or handle as per policy) If duplicate not allowed, ignore. If allowed, insert in appropriate leaf.

Step 12: Insert 45 45 > 18, 45 < 51, go to [19,35,39] Insert 45 → [19,35,39,45] → Full! Split at median (35)

                [18, 35, 51, 74]
               /   |    |    |    \
           [10] [19] [39,45] [58,60,72] [76,87]

Final B-Tree of Order 4:

                [18, 35, 51, 74]
               /   |    |    |    \
           [10] [19] [39,45] [58,60,72] [76,87]

Question 28: AVL Tree Insertion with Rotations

Insert the following keys in the order given below to build them into an AVL tree: [WBUT 2014, 2019] 8 12 9 11 7 6 66 2 1 44 Clearly mention different rotation used and balance factor of each node.

Answer:

Step-by-step AVL Tree Construction:

Step 1: Insert 8

8(0)

Step 2: Insert 12

8(1)
 \
 12(0)

Step 3: Insert 9

  8(2) ← UNBALANCED (BF=2)
   \
   12(1)
   /
 9(0)

Rotation Required: RL Rotation (Right-Left)

  • First right rotate at 12

  • Then left rotate at 8

After RL rotation:

   9(0)
  / \
8(0)12(0)

Step 4: Insert 11

    9(1)
   / \
 8(0)12(1)
     /
   11(0)

Step 5: Insert 7

      9(1)
     / \
   8(1)12(1)
  /     /
7(0)  11(0)

Step 6: Insert 6

        9(2) ← UNBALANCED (BF=2)
       / \
     8(2)12(1)
    /     /
  7(1)  11(0)
  /
6(0)

Rotation Required: LL Rotation at 8 After LL rotation:

        9(1)
       / \
     7(1)12(1)
    / \   /
  6(0)8(0)11(0)

Step 7: Insert 66

        9(1)
       / \
     7(1)12(2) ← UNBALANCED
    / \   / \
  6(0)8(0)11(0)66(0)

Rotation Required: RR Rotation at 12 After RR rotation:

        9(1)
       / \
     7(1)11(1)
    / \     \
  6(0)8(0)  12(1)
              \
              66(0)

Step 8: Insert 2

          9(2) ← UNBALANCED
         / \
       7(2)11(1)
      / \     \
    6(1)8(0)  12(1)
    /           \
  2(0)          66(0)

Rotation Required: LL Rotation at 7 After LL rotation:

          9(1)
         / \
       6(1)11(1)
      / \     \
    2(0)7(1)  12(1)
        /       \
      8(0)      66(0)

Wait, check balance: 7 has BF=1? 7's left NULL, right 8(0) → BF=-1? Actually 7's height = 1 (right child 8), left NULL height 0, so BF = 0-1 = -1 (right high)

Step 9: Insert 1

          9(2) ← UNBALANCED
         / \
       6(2)11(1)
      / \     \
    2(1)7(1)  12(1)
    /   /       \
  1(0)8(0)      66(0)

Rotation Required: LL Rotation at 6 After LL rotation:

            9(1)
           / \
         2(1)11(1)
        / \     \
      1(0)6(1)  12(1)
          / \     \
        2(0)7(1)  66(0)
            /
          8(0)

Duplicate key 2? Actually we already have 2, so need to handle carefully.

Step 10: Insert 44

            9(1)
           / \
         2(1)11(2) ← UNBALANCED
        / \     \
      1(0)6(1)  12(2)
          / \     \
        2(0)7(1)  66(1)
            /     /
          8(0)  44(0)

Rotation Required: RL Rotation at 11

  • First right rotate at 12

  • Then left rotate at 11

After RL rotation:

               9(1)
              / \
            2(1)12(0)
           / \   / \
         1(0)6(1)11(1)66(1)
            / \     /   /
          2(0)7(1)44(0)
              /
            8(0)

Final AVL Tree:

               9
              / \
            2    12
           / \   / \
          1   6 11  66
             / \   /
            2   7 44
               /
              8

Balance Factors:

  • Node 9: height(left)=3, height(right)=2 → BF=1

  • Node 2: left=1, right=2 → BF=-1

  • Node 12: left=1, right=1 → BF=0

  • Node 1: leaf → BF=0

  • Node 6: left=1, right=2 → BF=-1

  • Node 11: leaf → BF=0

  • Node 66: left=1, right=0 → BF=1

  • Node 7: left=0, right=1 → BF=-1

  • Node 44: leaf → BF=0

  • Node 8: leaf → BF=0

Rotations Used:

  1. RL Rotation (Step 3) - when inserting 9

  2. LL Rotation (Step 6) - when inserting 6

  3. RR Rotation (Step 7) - when inserting 66

  4. LL Rotation (Step 8) - when inserting 2

  5. LL Rotation (Step 9) - when inserting 1

  6. RL Rotation (Step 10) - when inserting 44


Question 29: Tree from Postorder and Inorder

The in-order and post-order traversal sequences of nodes in a binary tree are given below: [WBUT 2015] Post-order: I E J F C G K L H D B A In-order: E I C F J B G D K H L A Construct the tree.

Answer:

Step-by-step construction:

Step 1: Last element in postorder is root → A

Step 2: Split inorder using root A

  • Inorder: E I C F J B G D K H L A

  • Left subtree: E I C F J B G D K H L

  • Right subtree: (empty)

Step 3: Process left subtree (E I C F J B G D K H L)

  • From postorder before A: I E J F C G K L H D B

  • Last element in this group is root of left subtree → B

  • Inorder for left subtree: E I C F J B G D K H L

  • Split around B: Left: E I C F J, Right: G D K H L

Step 4: Process B's left (E I C F J)

  • From postorder remaining: I E J F C

  • Root of this subtree → C (last in this group)

  • Inorder for this subtree: E I C F J

  • Split around C: Left: E I, Right: F J

Step 5: Process C's left (E I)

  • From postorder remaining: I E

  • Root of this subtree → E (last in this group)

  • Inorder for this subtree: E I

  • Split around E: Left: empty, Right: I

Step 6: Process E's right (I)

  • I is leaf

Step 7: Process C's right (F J)

  • From postorder remaining: J F

  • Root of this subtree → F (last in this group)

  • Inorder for this subtree: F J

  • Split around F: Left: empty, Right: J

Step 8: Process F's right (J)

  • J is leaf

Step 9: Process B's right (G D K H L)

  • From postorder remaining: G K L H D

  • Root of this subtree → D (last in this group)

  • Inorder for this subtree: G D K H L

  • Split around D: Left: G, Right: K H L

Step 10: Process D's left (G)

  • G is leaf

Step 11: Process D's right (K H L)

  • From postorder remaining: K L H

  • Root of this subtree → H (last in this group)

  • Inorder for this subtree: K H L

  • Split around H: Left: K, Right: L

Step 12: Process H's left (K)

  • K is leaf

Step 13: Process H's right (L)

  • L is leaf

Final Binary Tree:

                A
               /
              B
             / \
            C   D
           / \   \
          E   F   H
           \   \ / \
            I   J K L

Verification:

Inorder (LNR): E, I, C, F, J, B, G, D, K, H, L, A ✓

  • Left of B: E I C F J

  • Right of B: G D K H L

  • Structure matches

Postorder (LRN): I, E, J, F, C, G, K, L, H, D, B, A ✓

  • Left subtree of B: I E J F C

  • Right subtree of B: G K L H D

  • Root A at end


Question 30: BST Min/Max Functions

Write a C function to find out the maximum and the minimum elements in a binary search tree. [WBUT 2016]

Answer:

Properties:

  • In a BST, the minimum value is the leftmost node

  • The maximum value is the rightmost node

C Functions:

#include <stdio.h>
#include <stdlib.h>

// Structure for BST node
struct node {
    struct node* lchild;  // left child
    int info;             // data
    struct node* rchild;  // right child
};

// Function to create a new node
struct node* createNode(int data) {
    struct node* newNode = (struct node*)malloc(sizeof(struct node));
    newNode->info = data;
    newNode->lchild = NULL;
    newNode->rchild = NULL;
    return newNode;
}

// Function to insert a node (for testing)
struct node* insert(struct node* root, int data) {
    if (root == NULL) {
        return createNode(data);
    }
    
    if (data < root->info) {
        root->lchild = insert(root->lchild, data);
    }
    else if (data > root->info) {
        root->rchild = insert(root->rchild, data);
    }
    
    return root;
}

// ---------- MINIMUM FUNCTIONS ----------

// Recursive function to find minimum
struct node* findMinRecursive(struct node* ptr) {
    // Base cases
    if (ptr == NULL) {
        return NULL;  // Empty tree
    }
    
    // If left child is NULL, current node is minimum
    if (ptr->lchild == NULL) {
        return ptr;
    }
    
    // Recursively go to left subtree
    return findMinRecursive(ptr->lchild);
}

// Iterative function to find minimum
struct node* findMinIterative(struct node* ptr) {
    // Check for empty tree
    if (ptr == NULL) {
        return NULL;
    }
    
    // Keep going left until no more left child
    while (ptr->lchild != NULL) {
        ptr = ptr->lchild;
    }
    
    return ptr;
}

// ---------- MAXIMUM FUNCTIONS ----------

// Recursive function to find maximum
struct node* findMaxRecursive(struct node* ptr) {
    // Base cases
    if (ptr == NULL) {
        return NULL;  // Empty tree
    }
    
    // If right child is NULL, current node is maximum
    if (ptr->rchild == NULL) {
        return ptr;
    }
    
    // Recursively go to right subtree
    return findMaxRecursive(ptr->rchild);
}

// Iterative function to find maximum
struct node* findMaxIterative(struct node* ptr) {
    // Check for empty tree
    if (ptr == NULL) {
        return NULL;
    }
    
    // Keep going right until no more right child
    while (ptr->rchild != NULL) {
        ptr = ptr->rchild;
    }
    
    return ptr;
}

// Function to print the results
void printMinMax(struct node* root) {
    struct node* minNode = findMinIterative(root);
    struct node* maxNode = findMaxIterative(root);
    
    if (minNode != NULL) {
        printf("Minimum element: %d\n", minNode->info);
    } else {
        printf("Tree is empty\n");
    }
    
    if (maxNode != NULL) {
        printf("Maximum element: %d\n", maxNode->info);
    }
}

// Inorder traversal to verify BST
void inorder(struct node* root) {
    if (root != NULL) {
        inorder(root->lchild);
        printf("%d ", root->info);
        inorder(root->rchild);
    }
}

// Main function to demonstrate
int main() {
    struct node* root = NULL;
    
    // Insert some values
    int values[] = {50, 30, 70, 20, 40, 60, 80, 10, 90};
    int n = sizeof(values) / sizeof(values[0]);
    
    for (int i = 0; i < n; i++) {
        root = insert(root, values[i]);
    }
    
    printf("BST (Inorder): ");
    inorder(root);
    printf("\n\n");
    
    // Find minimum using both methods
    struct node* min1 = findMinRecursive(root);
    struct node* min2 = findMinIterative(root);
    printf("Minimum (recursive): %d\n", min1->info);
    printf("Minimum (iterative): %d\n", min2->info);
    
    // Find maximum using both methods
    struct node* max1 = findMaxRecursive(root);
    struct node* max2 = findMaxIterative(root);
    printf("Maximum (recursive): %d\n", max1->info);
    printf("Maximum (iterative): %d\n", max2->info);
    
    return 0;
}

Example Output:

BST (Inorder): 10 20 30 40 50 60 70 80 90

Minimum (recursive): 10
Minimum (iterative): 10
Maximum (recursive): 90
Maximum (iterative): 90

Time Complexity:

  • Both operations: O(h) where h is height

  • Best/Average: O(log n)

  • Worst: O(n) for skewed tree

Space Complexity:

  • Recursive: O(h) for recursion stack

  • Iterative: O(1)

Example Walkthrough:

Consider BST:

        50
       /  \
     30    70
    /  \   / \
   20  40 60 80
  /
10

Finding Minimum:

  1. Start at 50 → go left to 30

  2. From 30 → go left to 20

  3. From 20 → go left to 10

  4. 10 has no left child → return 10

Finding Maximum:

  1. Start at 50 → go right to 70

  2. From 70 → go right to 80

  3. 80 has no right child → return 80


Question 31: Why Preorder + Postorder Cannot Reconstruct Tree

Given the pre-order sequence and the post-order sequence, why cannot you reconstruct the tree? [WBUT 2016]

Answer:

Explanation:

It is impossible to construct a unique binary tree from only preorder and postorder traversals. This is a fundamental limitation of tree reconstruction.

Reason 1: Ambiguity with Single Children

When a node has only one child, preorder and postorder cannot distinguish whether it's a left child or right child.

Example:

Tree A:        Tree B:
    5             5
   /               \
  3                 3

Both trees produce:

  • Preorder: 5, 3

  • Postorder: 3, 5

Reason 2: Subtree Partition Ambiguity

Preorder gives: [Root] + [Left subtree] + [Right subtree] Postorder gives: [Left subtree] + [Right subtree] + [Root]

Without knowing which nodes belong to left vs right subtree, multiple partitions are possible.

Example with 3 nodes:

Consider trees:

Tree 1:         Tree 2:         Tree 3:
    2              2                2
   / \            /                  \
  1   3          1                    3

All three produce:

  • Preorder: 2, 1, 3

  • Postorder: 1, 3, 2

Mathematical Proof:

Let T be a binary tree with n nodes.

  • Preorder: Root + Preorder(Left) + Preorder(Right)

  • Postorder: Postorder(Left) + Postorder(Right) + Root

If either Left or Right subtree is empty, we cannot determine which one is empty.

Counter-example with 4 nodes:

Tree X:           Tree Y:
    4                4
   / \              / \
  2   5            2   5
 / \                  /
1   3                3
                     /
                    1

Can you find traversals that are identical? Try constructing and you'll see ambiguity.

What is needed for unique reconstruction?

To uniquely reconstruct a binary tree, we need:

  1. Inorder + Preorder OR

  2. Inorder + Postorder

Why Inorder resolves ambiguity: Inorder traversal gives the exact position of root with respect to left and right subtrees. The root splits inorder sequence into left and right parts unambiguously.

Example with Inorder + Preorder:

Preorder: F B A D C E G I H
Inorder:  A B C D E F G H I

We know:
- F is root
- Left of F in inorder: A B C D E
- Right of F in inorder: G H I
This uniquely determines the structure.

Special Case - Full Binary Trees:

For full binary trees (every node has 0 or 2 children), preorder + postorder CAN uniquely determine the tree. But for general binary trees, it's impossible.

Conclusion:

  • Preorder + Postorder → Not sufficient for unique reconstruction

  • Inorder + (Preorder or Postorder) → Sufficient for unique reconstruction


Question 32: Binary Search Tree Definition and Construction

a) What do you mean by a binary search tree? [WBUT 2016] b) Construct a binary search tree by inserting the list of elements one by one: 13, 10, 3, 5, 18, 15, 14 c) Write an algorithm for pre-order traversal of a tree represented by a linked-list.

Answer:

Part a) Binary Search Tree Definition

A Binary Search Tree (BST) is a binary tree that is either empty or satisfies the following conditions for every node:

  1. Left Subtree Property: All nodes in the left subtree have values less than the parent node

  2. Right Subtree Property: All nodes in the right subtree have values greater than the parent node

  3. Recursive Property: Both left and right subtrees are also BSTs

Formal Definition:

BST = ∅ OR (root, leftBST, rightBST) such that:
∀x ∈ leftBST: x < root
∀y ∈ rightBST: y > root

Properties:

  • No duplicate keys (typically)

  • Inorder traversal gives sorted order

  • Search, Insert, Delete operations are O(h) where h is height

Example:

        50
       /  \
     30    70
    /  \   / \
   20  40 60 80

Part b) BST Construction

Insert elements: 13, 10, 3, 5, 18, 15, 14

Step 1: Insert 13

13

Step 2: Insert 10 (10 < 13)

  13
 /
10

Step 3: Insert 3 (3 < 13, 3 < 10)

    13
   /
  10
 /
3

Step 4: Insert 5 (5 < 13, 5 < 10, 5 > 3)

      13
     /
    10
   /
  3
   \
    5

Step 5: Insert 18 (18 > 13)

      13
     /  \
    10   18
   /
  3
   \
    5

Step 6: Insert 15 (15 < 18, 15 > 13)

        13
       /  \
     10    18
    /     /
   3     15
    \
     5

Step 7: Insert 14 (14 < 15, 14 > 13)

          13
         /  \
       10    18
      /     /
     3     15
      \   /
       5 14

Final BST:

          13
         /  \
       10    18
      /     /
     3     15
      \   /
       5 14

Inorder Traversal: 3, 5, 10, 13, 14, 15, 18 (sorted)


Part c) Pre-order Traversal Algorithm (Linked List Representation)

Pre-order Traversal (Root-Left-Right):

  1. Visit the root node

  2. Traverse left subtree in preorder

  3. Traverse right subtree in preorder

Algorithm 1: Recursive Pre-order

Algorithm: preorderRecursive(root)
Input: root of binary tree (linked list representation)
Output: prints nodes in preorder

if root == NULL then
    return
end if

print root→data           // Visit root
preorderRecursive(root→left)   // Traverse left
preorderRecursive(root→right)  // Traverse right

Algorithm 2: Iterative Pre-order (using stack)

Algorithm: preorderIterative(root)
Input: root of binary tree
Output: prints nodes in preorder

if root == NULL then
    return
end if

Create empty stack S
push root onto S

while S is not empty do
    current = pop from S
    print current→data
    
    // Push right child first so that left is processed first
    if current→right != NULL then
        push current→right onto S
    end if
    
    if current→left != NULL then
        push current→left onto S
    end if
end while

C Implementation:

#include <stdio.h>
#include <stdlib.h>

// Structure for tree node (linked list representation)
struct node {
    int data;
    struct node* left;
    struct node* right;
};

// Stack structure for iterative traversal
struct stack {
    int top;
    struct node* array[100];
};

// Function to create a new node
struct node* createNode(int data) {
    struct node* newNode = (struct node*)malloc(sizeof(struct node));
    newNode->data = data;
    newNode->left = NULL;
    newNode->right = NULL;
    return newNode;
}

// Function to insert in BST (for testing)
struct node* insert(struct node* root, int data) {
    if (root == NULL) {
        return createNode(data);
    }
    
    if (data < root->data) {
        root->left = insert(root->left, data);
    }
    else if (data > root->data) {
        root->right = insert(root->right, data);
    }
    
    return root;
}

// ---------- RECURSIVE PREORDER ----------
void preorderRecursive(struct node* root) {
    if (root == NULL) {
        return;
    }
    
    printf("%d ", root->data);      // Visit root
    preorderRecursive(root->left);   // Traverse left
    preorderRecursive(root->right);  // Traverse right
}

// ---------- ITERATIVE PREORDER (using stack) ----------
void preorderIterative(struct node* root) {
    if (root == NULL) {
        return;
    }
    
    // Create stack
    struct stack s;
    s.top = -1;
    
    // Push root
    s.array[++s.top] = root;
    
    while (s.top >= 0) {
        // Pop and print
        struct node* current = s.array[s.top--];
        printf("%d ", current->data);
        
        // Push right child first (so left is processed first)
        if (current->right != NULL) {
            s.array[++s.top] = current->right;
        }
        
        // Push left child
        if (current->left != NULL) {
            s.array[++s.top] = current->left;
        }
    }
}

// Function to build tree from part (b)
struct node* buildExampleTree() {
    struct node* root = NULL;
    int values[] = {13, 10, 3, 5, 18, 15, 14};
    int n = sizeof(values) / sizeof(values[0]);
    
    for (int i = 0; i < n; i++) {
        root = insert(root, values[i]);
    }
    
    return root;
}

// Main function to demonstrate
int main() {
    struct node* root = buildExampleTree();
    
    printf("Tree structure (from part b):\n");
    printf("          13\n");
    printf("         /  \\\n");
    printf("       10    18\n");
    printf("      /     /\n");
    printf("     3     15\n");
    printf("      \\   /\n");
    printf("       5 14\n\n");
    
    printf("Recursive Preorder: ");
    preorderRecursive(root);
    printf("\n");
    
    printf("Iterative Preorder: ");
    preorderIterative(root);
    printf("\n");
    
    return 0;
}

Output:

Tree structure (from part b):
          13
         /  \
       10    18
      /     /
     3     15
      \   /
       5 14

Recursive Preorder: 13 10 3 5 18 15 14
Iterative Preorder: 13 10 3 5 18 15 14

Time Complexity: O(n) for both methods Space Complexity:

  • Recursive: O(h) where h is height (recursion stack)

  • Iterative: O(n) worst case for stack


Question 33: AVL Tree with String Data

a) What is an AVL tree? [WBUT 2017] b) Construct an AVL search tree for the data list: AND, BEGIN, CASE, DO, END, FOR, GOTO c) For the AVL tree you have constructed delete the following keys in the order: DO, FOR, END

Answer:

Part a) AVL Tree Definition

An AVL tree (named after Adelson-Velsky and Landis) is a self-balancing binary search tree where the heights of left and right subtrees of every node differ by at most 1.

Properties:

  1. It is a BST (left < root < right)

  2. Balance Factor (BF) = height(left) - height(right) ∈ {-1, 0, 1}

  3. Every node maintains this balance property

  4. Rotations are performed when BF becomes ±2

Balance Factor Examples:

  • BF = 0: Both subtrees have same height

  • BF = +1: Left subtree is taller by 1

  • BF = -1: Right subtree is taller by 1


Part b) AVL Tree Construction with String Data

Insert in order: AND, BEGIN, CASE, DO, END, FOR, GOTO (Alphabetical order: AND < BEGIN < CASE < DO < END < FOR < GOTO)

Step 1: Insert AND

AND(0)

Step 2: Insert BEGIN (BEGIN > AND)

AND(1)
  \
  BEGIN(0)

Step 3: Insert CASE (CASE > AND, CASE > BEGIN)

AND(2) ← UNBALANCED (BF=2)
  \
  BEGIN(1)
    \
    CASE(0)

Rotation Required: Left Rotation (LL) at AND After rotation:

   BEGIN(0)
  /       \
AND(0)   CASE(0)

Step 4: Insert DO (DO > BEGIN, DO > CASE)

   BEGIN(1)
  /       \
AND(0)   CASE(1)
           \
           DO(0)

Step 5: Insert END (END > BEGIN, END > CASE, END > DO)

   BEGIN(2) ← UNBALANCED (BF=2)
  /       \
AND(0)   CASE(2)
           \
           DO(1)
             \
             END(0)

Rotation Required: Left Rotation at CASE After rotation:

      BEGIN(1)
     /       \
   AND(0)    DO(0)
            /   \
         CASE(0) END(0)

Step 6: Insert FOR (FOR > BEGIN, FOR > DO, FOR > END)

      BEGIN(2) ← UNBALANCED
     /       \
   AND(0)    DO(1)
            /   \
         CASE(0) END(1)
                  \
                  FOR(0)

Rotation Required: Left Rotation at DO After rotation:

         BEGIN(1)
        /       \
      AND(0)    END(0)
               /   \
            DO(1)   FOR(0)
           /   \
       CASE(0) 

Wait, need to check: END's left is DO(1), right is FOR(0) → BF=1 ✓

Step 7: Insert GOTO (GOTO > BEGIN, GOTO > END, GOTO > FOR)

         BEGIN(2) ← UNBALANCED
        /       \
      AND(0)    END(1)
               /   \
            DO(1)   FOR(1)
           /   \       \
       CASE(0)         GOTO(0)

Rotation Required: Left Rotation at END After rotation:

            BEGIN(1)
           /       \
         AND(0)    FOR(0)
                  /   \
               END(1) GOTO(0)
              /   \
            DO(1) 
           /   \
       CASE(0)

Final AVL Tree:

            BEGIN(1)
           /       \
         AND(0)    FOR(0)
                  /   \
               END(1) GOTO(0)
              /   \
            DO(1) 
           /   \
       CASE(0)

Balance Factors:

  • BEGIN: left height=1, right height=2 → BF=-1? Wait, left: AND(0) height=1, right: FOR height=2 → BF = 1-2 = -1 (right heavy)

  • FOR: left height=2, right height=1 → BF=1 (left heavy)

  • END: left height=2, right height=0 → BF=2? This indicates need for rebalancing!

Let's recalculate properly:

Tree after inserting GOTO:

            BEGIN
           /     \
        AND       FOR
                /     \
              END      GOTO
             /   \
           DO     
          /   \
        CASE

Heights:

  • CASE: 1

  • DO: 2 (left child CASE)

  • END: 3 (left child DO, right NULL)

  • FOR: 4 (left child END height 3, right child GOTO height 1) → BF=2 (unbalanced)

Need RL Rotation at FOR:

First right rotate at END:

            BEGIN
           /     \
        AND       FOR
                /     \
              DO       GOTO
             /   \
           CASE   END

Then left rotate at FOR:

               BEGIN
              /     \
           AND       DO
                    /   \
                 CASE    FOR
                        /   \
                      END    GOTO

Final Balanced AVL Tree:

               BEGIN(0)
              /     \
           AND(0)   DO(1)
                    /   \
                 CASE(0) FOR(0)
                        /   \
                      END(0) GOTO(0)

Part c) Deletion from AVL Tree

Initial Tree:

               BEGIN
              /     \
           AND       DO
                    /   \
                 CASE    FOR
                        /   \
                      END    GOTO

Step 1: Delete DO

Find inorder successor of DO: smallest in right subtree = END Replace DO with END, then delete END from its position

After replacing:

               BEGIN
              /     \
           AND       END
                    /   \
                 CASE    FOR
                        /   \
                      ?     GOTO

Delete END from original position (leaf? No, END had right child FOR)

Actually, when deleting DO (which has two children):

  1. Find inorder successor (END)

  2. Copy END's value to DO

  3. Delete END from its original position

Original END had right child FOR, so after deleting END:

               BEGIN
              /     \
           AND       END
                    /   \
                 CASE    FOR
                           \
                           GOTO

Check balance at FOR: left NULL, right GOTO → BF=-1 (OK) Check balance at END: left height=2 (CASE→?), right height=2 (FOR→GOTO) → BF=0 Check balance at BEGIN: left height=1, right height=3 → BF=-2 (UNBALANCED)

Rotation needed at BEGIN: Left subtree height=1, right subtree height=3 → Right-Left (RL) rotation

After RL rotation:

                 END
                /   \
            BEGIN    FOR
            /   \       \
          AND   CASE    GOTO

Step 2: Delete FOR

FOR is leaf (no children) → simply remove

                 END
                /   \
            BEGIN    ?
            /   \    
          AND   CASE 

After deletion:

                 END
                /   
            BEGIN    
            /   \    
          AND   CASE

Check balance at END: left height=2, right height=0 → BF=2 (UNBALANCED)

Rotation needed at END: Right Rotation (RR)

After RR rotation:

                BEGIN
               /     \
             AND      END
                       \
                       CASE

Step 3: Delete END

END has one child (CASE) → replace END with CASE

                BEGIN
               /     \
             AND      CASE

Final tree after all deletions:

                BEGIN
               /     \
             AND      CASE

Question 34: AVL Rotation Mechanisms

Explain different rotation mechanisms in AVL tree. [WBUT 2019]

Answer:

Introduction to AVL Rotations:

Rotations are operations performed on AVL trees to restore balance when the balance factor of any node becomes +2 or -2 after insertion or deletion. There are four types of rotations, classified into two categories:

  1. Single Rotations: LL and RR

  2. Double Rotations: LR and RL

Balance Factor Recap: BF = height(left) - height(right)

  • BF = +2 → Left heavy (needs right rotation)

  • BF = -2 → Right heavy (needs left rotation)


1. Single Left Rotation (LL Rotation)

When to use: When a node is inserted in the right subtree of the right child (Right-Right case)

Situation:

  • Node A has BF = -2

  • A's right child B has BF = -1 or 0

  • Insertion happened in B's right subtree

Diagram:

Before Rotation:        After Rotation:
    A (BF=-2)               B (BF=0)
     \                     / \
      B (BF=-1)           A   C
       \
        C

Algorithm:

Algorithm: leftRotate(A)
    B = A→right
    T2 = B→left
    
    // Perform rotation
    B→left = A
    A→right = T2
    
    // Update heights
    updateHeight(A)
    updateHeight(B)
    
    return B  // New root

Example with numbers:

    30                     40
      \                   /  \
      40        →       30    50
        \
        50

2. Single Right Rotation (RR Rotation)

When to use: When a node is inserted in the left subtree of the left child (Left-Left case)

Situation:

  • Node A has BF = +2

  • A's left child B has BF = +1 or 0

  • Insertion happened in B's left subtree

Diagram:

Before Rotation:        After Rotation:
       A (BF=+2)            B (BF=0)
      /                    / \
     B (BF=+1)            C   A
    /
   C

Algorithm:

Algorithm: rightRotate(A)
    B = A→left
    T2 = B→right
    
    // Perform rotation
    B→right = A
    A→left = T2
    
    // Update heights
    updateHeight(A)
    updateHeight(B)
    
    return B  // New root

Example with numbers:

      50                   40
     /                    /  \
    40         →         30   50
   /
  30

3. Left-Right Rotation (LR Rotation)

When to use: When a node is inserted in the right subtree of the left child (Left-Right case)

Situation:

  • Node A has BF = +2

  • A's left child B has BF = -1

  • Insertion happened in B's right subtree

Diagram:

Before Rotation:        Step 1: Left rotate at B
       A                      A
      /                      /
     B (BF=-1)              C
      \                    / \
       C                  B   ?
        \
         ?
         
Step 2: Right rotate at A
         C
        / \
       B   A

Algorithm:

Algorithm: leftRightRotate(A)
    // First left rotate at A's left child
    A→left = leftRotate(A→left)
    
    // Then right rotate at A
    return rightRotate(A)

Example with numbers:

    50                   50                 40
   /                    /                   / \
  30        →          40        →        30   50
   \                  /
    40               30

4. Right-Left Rotation (RL Rotation)

When to use: When a node is inserted in the left subtree of the right child (Right-Left case)

Situation:

  • Node A has BF = -2

  • A's right child B has BF = +1

  • Insertion happened in B's left subtree

Diagram:

Before Rotation:        Step 1: Right rotate at B
    A                       A
     \                       \
      B (BF=+1)               C
     /                       / \
    C                       ?   B
   /
  ?
  
Step 2: Left rotate at A
         C
        / \
       A   B

Algorithm:

Algorithm: rightLeftRotate(A)
    // First right rotate at A's right child
    A→right = rightRotate(A→right)
    
    // Then left rotate at A
    return leftRotate(A)

Example with numbers:

  30                   30                 40
    \                    \                / \
    50        →          40       →      30  50
    /                      \
  40                       50

Summary Table:

Rotation Type

When BF becomes

Case

Steps

LL (Left-Left)

+2 at node, +1 at left child

Insert in left of left

Single right rotation

RR (Right-Right)

-2 at node, -1 at right child

Insert in right of right

Single left rotation

LR (Left-Right)

+2 at node, -1 at left child

Insert in right of left

Left then right

RL (Right-Left)

-2 at node, +1 at right child

Insert in left of right

Right then left

C Implementation:

// Structure for AVL node
struct Node {
    int key;
    struct Node* left;
    struct Node* right;
    int height;
};

// Function to get height
int height(struct Node* n) {
    if (n == NULL) return 0;
    return n->height;
}

// Function to get balance factor
int getBalance(struct Node* n) {
    if (n == NULL) return 0;
    return height(n->left) - height(n->right);
}

// Right rotate
struct Node* rightRotate(struct Node* y) {
    struct Node* x = y->left;
    struct Node* T2 = x->right;
    
    // Perform rotation
    x->right = y;
    y->left = T2;
    
    // Update heights
    y->height = max(height(y->left), height(y->right)) + 1;
    x->height = max(height(x->left), height(x->right)) + 1;
    
    return x; // New root
}

// Left rotate
struct Node* leftRotate(struct Node* x) {
    struct Node* y = x->right;
    struct Node* T2 = y->left;
    
    // Perform rotation
    y->left = x;
    x->right = T2;
    
    // Update heights
    x->height = max(height(x->left), height(x->right)) + 1;
    y->height = max(height(y->left), height(y->right)) + 1;
    
    return y; // New root
}

Question 35: Proof of Leaf Nodes Relationship

Show that for a non-empty binary tree, if n₀ is the number of leaf nodes and n₂ is the number of nodes of degree 2 then n₀ = n₂ + 1. [WBUT 2019]

Answer:

Theorem: In any non-empty binary tree, the number of leaf nodes (nodes with degree 0) is exactly one more than the number of nodes with degree 2.

Proof:

Let:

  • n = total number of nodes in the binary tree

  • n₀ = number of nodes with degree 0 (leaf nodes)

  • n₁ = number of nodes with degree 1

  • n₂ = number of nodes with degree 2

Step 1: Total nodes equation

n = n₀ + n₁ + n₂  ... (1)

Step 2: Branches (edges) relationship

Every node except the root has exactly one incoming edge (branch). Let B be the total number of branches (edges) in the tree.

Therefore:

n = B + 1  ... (2)

Step 3: Branches count from degree perspective

Each node contributes branches equal to its degree:

  • Nodes with degree 0 contribute 0 branches

  • Nodes with degree 1 contribute 1 branch each

  • Nodes with degree 2 contribute 2 branches each

So:

B = 0·n₀ + 1·n₁ + 2·n₂
B = n₁ + 2n₂  ... (3)

Step 4: Substitute equation (3) into equation (2)

n = (n₁ + 2n₂) + 1  ... (4)

Step 5: Equate equations (1) and (4)

n₀ + n₁ + n₂ = n₁ + 2n₂ + 1

Step 6: Simplify

n₀ + n₁ + n₂ - n₁ - 2n₂ = 1
n₀ - n₂ = 1

Therefore:

n₀ = n₂ + 1

Hence proved.


Verification with Examples:

Example 1: Full Binary Tree

        A (degree 2)
       / \
      B   C (degree 2)
     / \   \
    D   E   F (degree 0)
   (0) (0) (0)

Count:

  • n₀ = 3 (D, E, F)

  • n₁ = 0

  • n₂ = 2 (A, C)

  • Check: 3 = 2 + 1 ✓

Example 2: Skewed Tree

    A (degree 1)
     \
      B (degree 1)
       \
        C (degree 1)
         \
          D (degree 0)

Count:

  • n₀ = 1 (D)

  • n₁ = 3 (A, B, C)

  • n₂ = 0

  • Check: 1 = 0 + 1 ✓

Example 3: Complete Binary Tree

        A (degree 2)
       / \
      B   C (degree 2)
     / \ / \
    D  E F  G (degree 0)
   (0)(0)(0)(0)

Count:

  • n₀ = 4 (D, E, F, G)

  • n₁ = 0

  • n₂ = 3 (A, B, C)

  • Check: 4 = 3 + 1 ✓

Example 4: Tree with degree 1 nodes

        A (degree 2)
       / \
      B   C (degree 1)
     / \   \
    D   E   F (degree 0)
   (0) (0) (0)

Count:

  • n₀ = 3 (D, E, F)

  • n₁ = 1 (C)

  • n₂ = 2 (A, B)

  • Check: 3 = 2 + 1 ✓


Alternative Proof using Induction:

Base Case: Tree with single node

  • n₀ = 1, n₂ = 0 → 1 = 0 + 1 ✓

Inductive Step: Assume true for all trees with < m nodes Consider tree T with m nodes, root R

Case 1: Root has two children

  • Left subtree L has n₀L leaf nodes, n₂L degree-2 nodes

  • Right subtree R has n₀R leaf nodes, n₂R degree-2 nodes

  • By induction: n₀L = n₂L + 1, n₀R = n₂R + 1

  • For whole tree: n₀ = n₀L + n₀R, n₂ = n₂L + n₂R + 1

  • Therefore: n₀ = (n₂L + 1) + (n₂R + 1) = (n₂L + n₂R) + 2 = (n₂ - 1) + 2 = n₂ + 1 ✓

Case 2: Root has one child

  • Similar reasoning leads to same result

Case 3: Root is leaf

  • Already covered in base case

Corollary: In a full binary tree (where every node has either 0 or 2 children), n₁ = 0, so n₀ = n₂ + 1, and total nodes n = n₀ + n₂ = 2n₂ + 1, which is always odd.


Question 36: BST Construction and Deletion

a) Create a binary search tree using the following data elements: [WBUT 2019] 54, 60, 69, 32, 46, 25, 81, 29, 75, 78 b) Explain with example the process of deleting a node from a binary search tree.

Answer:

Part a) BST Construction

Insert elements in order: 54, 60, 69, 32, 46, 25, 81, 29, 75, 78

Step 1: Insert 54

54

Step 2: Insert 60 (60 > 54)

54
 \
 60

Step 3: Insert 69 (69 > 54, 69 > 60)

54
 \
 60
  \
  69

Step 4: Insert 32 (32 < 54)

   54
  /  \
32   60
      \
      69

Step 5: Insert 46 (46 < 54, 46 > 32)

     54
    /  \
  32   60
    \    \
    46    69

Step 6: Insert 25 (25 < 54, 25 < 32)

       54
      /  \
    32   60
   /  \    \
 25   46    69

Step 7: Insert 81 (81 > 54, 81 > 60, 81 > 69)

       54
      /  \
    32   60
   /  \    \
 25   46    69
             \
             81

Step 8: Insert 29 (29 < 54, 29 < 32, 29 > 25)

          54
         /  \
       32   60
      /  \    \
    25   46    69
      \         \
      29         81

Step 9: Insert 75 (75 > 54, 75 > 60, 75 > 69, 75 < 81)

          54
         /  \
       32   60
      /  \    \
    25   46    69
      \         \
      29         81
                /
              75

Step 10: Insert 78 (78 > 54, 78 > 60, 78 > 69, 78 < 81, 78 > 75)

          54
         /  \
       32   60
      /  \    \
    25   46    69
      \         \
      29         81
                /
              75
                \
                78

Final BST:

          54
         /  \
       32   60
      /  \    \
    25   46    69
      \         \
      29         81
                /
              75
                \
                78

Inorder Traversal: 25, 29, 32, 46, 54, 60, 69, 75, 78, 81 (sorted)


Part b) Deletion from BST - Three Cases

Case 1: Deleting a Leaf Node (no children)

Delete node with value 29 from the tree

Before:                     After:
      54                      54
     /  \                    /  \
   32   60                  32   60
  /  \    \                /  \    \
25   46    69             25   46    69
  \         \                       \
  29         81                      81
            /                       /
          75                       75
            \                        \
            78                        78

Algorithm:

  1. Locate node 29

  2. Set parent's (32) left pointer to NULL

  3. Free node 29


Case 2: Deleting a Node with One Child

Delete node with value 81 from the tree (81 has one child 75)

Before:                     After:
      54                      54
     /  \                    /  \
   32   60                  32   60
  /  \    \                /  \    \
25   46    69             25   46    69
      \     \                    \
      29    81                    75
           /                        \
         75                          78
           \
           78

Algorithm:

  1. Locate node 81

  2. Connect parent (69) directly to child (75)

  3. Free node 81


Case 3: Deleting a Node with Two Children

Delete node with value 54 (root) from the tree

Two approaches:

  • Replace with inorder successor (smallest in right subtree)

  • Replace with inorder predecessor (largest in left subtree)

Using Inorder Successor:

Inorder successor of 54 is 60 (smallest in right subtree)

Step 1: Copy successor's value       Step 2: Delete successor
Before:                              After:
      54 (to delete)                      60
     /  \                                /  \
   32   60                              32   69
  /  \    \                            /  \    \
25   46    69                         25   46    81
      \     \                              \   /
      29    81                              29 75
           /                                    \
         75                                      78
           \
           78

Algorithm for node with two children:

deleteNode(root, key):
    if root == NULL: return root
    
    // Find the node
    if key < root.data:
        root.left = deleteNode(root.left, key)
    else if key > root.data:
        root.right = deleteNode(root.right, key)
    else:
        // Node found - handle three cases
        
        // Case 1: No child
        if root.left == NULL and root.right == NULL:
            free(root)
            return NULL
            
        // Case 2: One child
        if root.left == NULL:
            temp = root.right
            free(root)
            return temp
        if root.right == NULL:
            temp = root.left
            free(root)
            return temp
            
        // Case 3: Two children
        temp = findMin(root.right)  // inorder successor
        root.data = temp.data
        root.right = deleteNode(root.right, temp.data)
    
    return root

Complete C Implementation:

#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node* left;
    struct Node* right;
};

// Create new node
struct Node* createNode(int data) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->data = data;
    newNode->left = NULL;
    newNode->right = NULL;
    return newNode;
}

// Insert in BST
struct Node* insert(struct Node* root, int data) {
    if (root == NULL) return createNode(data);
    
    if (data < root->data)
        root->left = insert(root->left, data);
    else if (data > root->data)
        root->right = insert(root->right, data);
    
    return root;
}

// Find minimum value node
struct Node* findMin(struct Node* root) {
    while (root->left != NULL)
        root = root->left;
    return root;
}

// Delete node from BST
struct Node* deleteNode(struct Node* root, int key) {
    if (root == NULL) return root;
    
    // Find the node to delete
    if (key < root->data)
        root->left = deleteNode(root->left, key);
    else if (key > root->data)
        root->right = deleteNode(root->right, key);
    else {
        // Node found - handle three cases
        
        // Case 1: No child (leaf)
        if (root->left == NULL && root->right == NULL) {
            free(root);
            return NULL;
        }
        
        // Case 2: One child
        else if (root->left == NULL) {
            struct Node* temp = root->right;
            free(root);
            return temp;
        }
        else if (root->right == NULL) {
            struct Node* temp = root->left;
            free(root);
            return temp;
        }
        
        // Case 3: Two children
        else {
            // Find inorder successor (smallest in right subtree)
            struct Node* temp = findMin(root->right);
            
            // Copy successor's data
            root->data = temp->data;
            
            // Delete the successor
            root->right = deleteNode(root->right, temp->data);
        }
    }
    return root;
}

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

// Main function
int main() {
    struct Node* root = NULL;
    int values[] = {54, 60, 69, 32, 46, 25, 81, 29, 75, 78};
    int n = sizeof(values) / sizeof(values[0]);
    
    // Build BST
    for (int i = 0; i < n; i++) {
        root = insert(root, values[i]);
    }
    
    printf("Original BST (inorder): ");
    inorder(root);
    printf("\n");
    
    // Case 1: Delete leaf (29)
    root = deleteNode(root, 29);
    printf("After deleting 29: ");
    inorder(root);
    printf("\n");
    
    // Case 2: Delete node with one child (81)
    root = deleteNode(root, 81);
    printf("After deleting 81: ");
    inorder(root);
    printf("\n");
    
    // Case 3: Delete node with two children (54)
    root = deleteNode(root, 54);
    printf("After deleting 54: ");
    inorder(root);
    printf("\n");
    
    return 0;
}

Output:

Original BST (inorder): 25 29 32 46 54 60 69 75 78 81
After deleting 29: 25 32 46 54 60 69 75 78 81
After deleting 81: 25 32 46 54 60 69 75 78
After deleting 54: 25 32 46 60 69 75 78

Question 37: Short Notes

Write short notes on the following: [WBUT 2019] a) AVL Tree b) Threaded Binary Tree c) B-tree d) Binary Search Tree

Answer:


a) AVL Tree

Definition: AVL Tree (Adelson-Velsky and Landis tree) is a self-balancing binary search tree where the heights of left and right subtrees of every node differ by at most 1.

Properties:

  1. It is a BST (left < root < right)

  2. Balance Factor (BF) = height(left) - height(right) ∈ {-1, 0, 1}

  3. Every node maintains this balance property

  4. Rotations performed when BF becomes ±2

Balance Factor:

  • BF = 0: Both subtrees equal height

  • BF = +1: Left subtree taller by 1

  • BF = -1: Right subtree taller by 1

Rotations (4 types):

  1. LL Rotation: Single right rotation (Left-Left case)

  2. RR Rotation: Single left rotation (Right-Right case)

  3. LR Rotation: Left then right rotation (Left-Right case)

  4. RL Rotation: Right then left rotation (Right-Left case)

Time Complexity:

  • Search: O(log n)

  • Insert: O(log n)

  • Delete: O(log n)

Space Complexity: O(n)

Advantages:

  • Guaranteed O(log n) operations

  • Faster than BST for dynamic data

  • Self-balancing property

Disadvantages:

  • Extra space for balance factor

  • Complex implementation

  • Rotations overhead

Applications:

  • Database indexing

  • In-memory dictionaries

  • Where guaranteed search time needed

Example AVL Tree:

        30 (BF=0)
       /  \
     20    40 (BF=0)
    /  \   / \
  10   25 35 50 (all BF=0)

b) Threaded Binary Tree

Definition: A Threaded Binary Tree is a binary tree where NULL pointers are replaced with threads (pointers) to other nodes to facilitate traversal without recursion or stack.

Types of Threads:

  1. Single Threaded: Only right NULL pointers replaced

  2. Double Threaded: Both left and right NULL pointers replaced

Thread Types:

  • Right Thread: Points to inorder successor

  • Left Thread: Points to inorder predecessor

Node Structure:

struct ThreadedNode {
    int data;
    struct ThreadedNode* left;
    struct ThreadedNode* right;
    int leftThread;   // 1 if left is thread, 0 if child
    int rightThread;  // 1 if right is thread, 0 if child
};

Example:

Original Tree:        Threaded Tree (In-order):
      A                      A
     / \                    / \
    B   C                  B   C
   / \                    / \   \
  D   E                  D→B E→A C→?
       \                      \
        F                      F→C

Advantages:

  1. No stack required for traversal

  2. Faster traversal (no recursion overhead)

  3. Memory utilization of NULL pointers

  4. Easy to find successor/predecessor

Disadvantages:

  1. Complex insertion/deletion (threads need updating)

  2. Extra space for thread flags

  3. More complex implementation

Traversal Algorithm (In-order):

Algorithm: inorderThreaded(root)
    current = leftmost(root)
    while current != NULL:
        visit(current)
        if current.rightThread == 1:
            current = current.right
        else:
            current = leftmost(current.right)

Applications:

  • Where frequent traversals needed

  • Stack memory constraints

  • Real-time systems


c) B-Tree

Definition: A B-Tree of order m is a balanced m-way search tree optimized for disk storage where all leaves are at the same level.

Properties:

  1. All leaves at same level

  2. Each node (except root) has at least ⌈m/2⌉ children

  3. Each node has at most m children

  4. Internal node with k children has k-1 keys

  5. Keys in a node stored in ascending order

  6. Root has at least 2 children (if not leaf)

Node Structure:

[Key1, Key2, ..., Key(k-1)]
 /     |     |          \
Child1 Child2 Child3 ... Childk

Operations Time Complexity: O(log n)

Order Selection:

  • Order typically matches disk block size

  • Higher order = shorter tree = fewer disk accesses

Advantages:

  1. Optimized for disk I/O

  2. Automatically balanced

  3. Efficient for range queries

  4. Handles large datasets

  5. Predictable performance

Disadvantages:

  1. Complex implementation

  2. Space overhead

  3. Not efficient for in-memory data

Applications:

  • Database indexes (MySQL, Oracle)

  • File systems (NTFS, HFS+, ext4)

  • Directory structures

Example B-Tree of Order 4:

        [30, 60]
       /    |    \
  [10,20] [40,50] [70,80,90]

B-Tree vs B+ Tree:

  • B-Tree: Data pointers in all nodes

  • B+ Tree: Data pointers only in leaves, leaves linked


d) Binary Search Tree (BST)

Definition: A Binary Search Tree is a binary tree where for every node:

  • Left subtree contains nodes with values less than the node's value

  • Right subtree contains nodes with values greater than the node's value

  • Both subtrees are also BSTs

Properties:

  1. No duplicate keys (typically)

  2. Inorder traversal gives sorted order

  3. Dynamic set operations

Node Structure:

struct Node {
    int data;
    struct Node* left;
    struct Node* right;
};

Operations:

Operation

Average Case

Worst Case

Search

O(log n)

O(n)

Insert

O(log n)

O(n)

Delete

O(log n)

O(n)

Traversals:

  1. Inorder (LNR): Left-Root-Right → Sorted order

  2. Preorder (NLR): Root-Left-Right → Tree copy

  3. Postorder (LRN): Left-Right-Root → Tree deletion

Example BST:

        50
       /  \
     30    70
    /  \   / \
   20  40 60 80

Advantages:

  1. Simple implementation

  2. Efficient for random data

  3. Dynamic size

  4. Ordered operations

Disadvantages:

  1. Can become skewed (O(n) operations)

  2. No balance guarantee

  3. Performance depends on insertion order

Applications:

  • Symbol tables in compilers

  • Dictionary implementations

  • Priority queues

  • Sorting algorithms (Tree sort)

BST vs AVL:

  • BST: No balance requirement, simpler

  • AVL: Self-balancing, guaranteed O(log n)

Common Operations Implementation:

// Search
struct Node* search(struct Node* root, int key) {
    if (root == NULL || root->data == key)
        return root;
    if (key < root->data)
        return search(root->left, key);
    return search(root->right, key);
}

// Find minimum
struct Node* findMin(struct Node* root) {
    while (root->left != NULL)
        root = root->left;
    return root;
}

// Find maximum
struct Node* findMax(struct Node* root) {
    while (root->right != NULL)
        root = root->right;
    return root;
}
23 views