Bird
Raised Fist0
DSA Goprogramming

BST Inorder Predecessor in DSA Go

Choose your learning style10 modes available
Mental Model
The inorder predecessor of a node in a BST is the node that comes just before it in sorted order. It is the largest node smaller than the given node.
Analogy: Imagine a sorted list of books on a shelf. The inorder predecessor of a book is the book immediately to its left, the one that comes just before it alphabetically.
      5
     / \
    3   7
   / \   \
  2   4   8
↑curr=5
Dry Run Walkthrough
Input: BST: 5 -> 3 -> 7 -> 2 -> 4 -> 8, find inorder predecessor of node 5
Goal: Find the node that comes just before 5 in sorted order, which is 4
Step 1: Check if node 5 has left child; it does (3), so move to left subtree
      5
     / \
    3↑  7
   / \   \
  2   4   8
Why: Inorder predecessor is the rightmost node in left subtree
Step 2: Move right from 3 to 4, which has no right child
      5
     / \
    3   7
   / \↑  \
  2   4   8
Why: Rightmost node in left subtree is the inorder predecessor
Step 3: Stop at 4 since it has no right child; 4 is predecessor
      5
     / \
    3   7
   / \   \
  2  [4]  8
Why: Found largest node smaller than 5
Result:
4
Annotated Code
DSA Go
package main

import "fmt"

type TreeNode struct {
    Val   int
    Left  *TreeNode
    Right *TreeNode
}

func inorderPredecessor(root *TreeNode, p *TreeNode) *TreeNode {
    if p.Left != nil {
        // Move to left subtree
        curr := p.Left
        // Find rightmost node in left subtree
        for curr.Right != nil {
            curr = curr.Right
        }
        return curr
    }
    // If no left subtree, find deepest ancestor where p is in right subtree
    var predecessor *TreeNode
    curr := root
    for curr != nil {
        if p.Val > curr.Val {
            predecessor = curr
            curr = curr.Right
        } else if p.Val < curr.Val {
            curr = curr.Left
        } else {
            break
        }
    }
    return predecessor
}

func main() {
    // Build BST
    root := &TreeNode{5, nil, nil}
    root.Left = &TreeNode{3, &TreeNode{2, nil, nil}, &TreeNode{4, nil, nil}}
    root.Right = &TreeNode{7, nil, &TreeNode{8, nil, nil}}

    p := root // node with value 5
    pred := inorderPredecessor(root, p)
    if pred != nil {
        fmt.Printf("Inorder predecessor of %d is %d\n", p.Val, pred.Val)
    } else {
        fmt.Printf("Inorder predecessor of %d does not exist\n", p.Val)
    }
}
if p.Left != nil {
Check if node has left subtree to find predecessor there
curr := p.Left
Start at left child
for curr.Right != nil { curr = curr.Right }
Find rightmost node in left subtree
return curr
Return found predecessor in left subtree
var predecessor *TreeNode curr := root for curr != nil {
If no left subtree, search ancestors for predecessor
if p.Val > curr.Val { predecessor = curr; curr = curr.Right }
Move right and update predecessor when p is greater
else if p.Val < curr.Val { curr = curr.Left }
Move left when p is smaller
else { break }
Stop when node p is found
return predecessor
Return found predecessor or nil if none
OutputSuccess
Inorder predecessor of 5 is 4
Complexity Analysis
Time: O(h) because we traverse down the tree height h once to find predecessor
Space: O(1) because we use only a few pointers, no extra data structures
vs Alternative: Naive approach would do inorder traversal of entire tree O(n), this is faster by using BST properties
Edge Cases
Node has no left subtree and is the smallest node
No predecessor exists, function returns nil
DSA Go
return predecessor
Node has left subtree with multiple nodes
Function finds rightmost node in left subtree correctly
DSA Go
for curr.Right != nil { curr = curr.Right }
Node is root with no left child but has ancestors
Function finds deepest ancestor smaller than node
DSA Go
if p.Val > curr.Val { predecessor = curr; curr = curr.Right }
When to Use This Pattern
When asked to find the previous node in sorted order in a BST, reach for the inorder predecessor pattern because it uses BST structure to find the largest smaller node efficiently.
Common Mistakes
Mistake: Only checking left subtree and ignoring ancestor nodes when no left child exists
Fix: Add logic to traverse from root to find deepest ancestor smaller than target node
Mistake: Returning left child directly without finding rightmost node in left subtree
Fix: Traverse right children in left subtree to find the largest node
Summary
Finds the node just before a given node in BST sorted order.
Use when you need the previous smaller value relative to a node in BST.
If left subtree exists, predecessor is rightmost node there; else, it's deepest ancestor smaller than node.

Practice

(1/5)
1. What is the inorder predecessor of a node in a Binary Search Tree (BST)?
easy
A. The node with the smallest value larger than the given node
B. The node with the largest value smaller than the given node
C. The left child of the given node
D. The right child of the given node

Solution

  1. Step 1: Understand inorder traversal in BST

    Inorder traversal visits nodes in ascending order of their values.
  2. Step 2: Define inorder predecessor

    The inorder predecessor of a node is the node that comes just before it in this ascending order, meaning it has the largest value smaller than the node.
  3. Final Answer:

    The node with the largest value smaller than the given node -> Option B
  4. Quick Check:

    Inorder predecessor = largest smaller value [OK]
Hint: Predecessor is largest smaller value in BST [OK]
Common Mistakes:
  • Confusing predecessor with successor
  • Thinking predecessor is always left child
  • Assuming predecessor is the parent node
2. Which of the following Go function signatures correctly represents a function to find the inorder predecessor in a BST node struct named Node?
easy
A. func inorderPredecessor(root *Node, key int) *Node
B. func inorderPredecessor(key int) Node
C. func inorderPredecessor(root Node, key int) Node
D. func inorderPredecessor(root *Node) int

Solution

  1. Step 1: Analyze function parameters and return type

    The function needs the BST root pointer and the key to find predecessor for, and returns a pointer to the predecessor node.
  2. Step 2: Match with options

    func inorderPredecessor(root *Node, key int) *Node matches this: it takes *Node root, int key, and returns *Node.
  3. Final Answer:

    func inorderPredecessor(root *Node, key int) *Node -> Option A
  4. Quick Check:

    Pointer root and key input, pointer node output [OK]
Hint: Use pointer receiver and return pointer for BST nodes [OK]
Common Mistakes:
  • Passing root by value instead of pointer
  • Returning node value instead of node pointer
  • Missing key parameter to identify target node
3. Given the following BST and code snippet, what will be printed?
type Node struct {
    Val int
    Left, Right *Node
}

func inorderPredecessor(root *Node, key int) *Node {
    var predecessor *Node
    curr := root
    for curr != nil {
        if key <= curr.Val {
            curr = curr.Left
        } else {
            predecessor = curr
            curr = curr.Right
        }
    }
    return predecessor
}

func main() {
    root := &Node{Val: 20}
    root.Left = &Node{Val: 10}
    root.Right = &Node{Val: 30}
    root.Left.Left = &Node{Val: 5}
    root.Left.Right = &Node{Val: 15}

    pred := inorderPredecessor(root, 15)
    if pred != nil {
        fmt.Println(pred.Val)
    } else {
        fmt.Println("No predecessor")
    }
}
medium
A. No predecessor
B. 5
C. 15
D. 10

Solution

  1. Step 1: Trace the inorderPredecessor function for key=15

    Start at root (20). Since 15 <= 20, move left to 10. Since 15 > 10, set predecessor=10, move right to 15. Since 15 <= 15, move left (nil). Loop ends.
  2. Step 2: Return predecessor node value

    Predecessor is node with value 10, so print 10.
  3. Final Answer:

    10 -> Option D
  4. Quick Check:

    Largest smaller than 15 is 10 [OK]
Hint: Move left if key ≤ node, else update predecessor and move right [OK]
Common Mistakes:
  • Returning the key itself as predecessor
  • Confusing predecessor with successor
  • Not updating predecessor correctly when moving right
4. The following Go function is intended to find the inorder predecessor of a node with value key in a BST. What is the bug?
func inorderPredecessor(root *Node, key int) *Node {
    var predecessor *Node
    curr := root
    for curr != nil {
        if key < curr.Val {
            curr = curr.Left
        } else {
            predecessor = curr
            curr = curr.Right
        }
    }
    return predecessor
}
medium
A. The function should return curr instead of predecessor
B. The predecessor variable should be reset inside the loop
C. The condition should be key <= curr.Val to handle equal keys correctly
D. The loop should use recursion instead of iteration

Solution

  1. Step 1: Analyze the condition in the loop

    The current condition is key < curr.Val, which skips updating predecessor when key equals curr.Val.
  2. Step 2: Correct the condition to include equality

    Changing to key <= curr.Val ensures that when key equals curr.Val, we move left without updating predecessor, which is correct for inorder predecessor logic.
  3. Final Answer:

    The condition should be key <= curr.Val to handle equal keys correctly -> Option C
  4. Quick Check:

    Use <= to handle equal keys properly [OK]
Hint: Use <= in condition to handle equal keys correctly [OK]
Common Mistakes:
  • Using < instead of <= causes wrong predecessor for equal keys
  • Resetting predecessor inside loop loses previous valid value
  • Returning curr instead of predecessor returns wrong node
5. You have a BST where each node has a pointer to its parent. How would you find the inorder predecessor of a given node n efficiently without starting from the root? Choose the best approach.
hard
A. If n has a left child, find the rightmost node in its left subtree; else, move up using parent pointers until you find a node which is the right child of its parent
B. Always start from the root and do a full inorder traversal to find predecessor
C. If n has a right child, find the leftmost node in its right subtree; else, move up using parent pointers until you find a node which is the left child of its parent
D. Return the parent node directly as the predecessor

Solution

  1. Step 1: Check if node has left subtree

    If n has a left child, the predecessor is the rightmost node in that left subtree.
  2. Step 2: If no left child, move up using parent pointers

    Move up until you find a node that is the right child of its parent; that parent is the predecessor.
  3. Final Answer:

    If n has left child, find rightmost in left subtree; else move up until node is right child -> Option A
  4. Quick Check:

    Use left subtree or climb parents to find predecessor [OK]
Hint: Use left subtree or climb parents via right child link [OK]
Common Mistakes:
  • Starting from root wastes time
  • Confusing predecessor with successor logic
  • Returning parent directly without checks
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard