Lowest Common Ancestor Of A Binary Search Tree

To solve this coding challenge, we need to find the Lowest Common Ancestor (LCA) of two nodes in a Binary Search Tree (BST). In a BST, for each node, the value of its left subtree nodes will be lesser, and the value of its right subtree nodes will be greater than the node's value. This property of the BST can be leveraged to efficiently determine the LCA of two given nodes.
# Explanation
  1. Definition Recap :
    • The Lowest Common Ancestor (LCA) of two nodes \( p \) and \( q \) in a binary tree is the lowest node that has both \( p \) and \( q \) as descendants, including the node itself.
  2. Understanding the Binary Search Tree (BST) :
    • For a given node \( n \),
      n.left
      contains nodes with values less than
      n.val
      , and
      n.right
      contains nodes with values greater than
      n.val
      .
  3. LCA Property and BST Characteristics :
    • Given nodes \( p \) and \( q \):
      • If both \( p \) and \( q \) are less than \( n \), then the LCA must be in the left subtree of \( n \).
      • If both \( p \) and \( q \) are greater than \( n \), then the LCA must be in the right subtree of \( n \).
      • If one is less than \( n \) and the other is greater than \( n \), or one of them is equal to \( n \), then \( n \) is the LCA.
  4. Algorithm :
    • Start with the root of the tree.
    • Traverse the tree:
      • If both nodes are smaller than the current node, move to the left child.
      • If both nodes are larger than the current node, move to the right child.
      • If the nodes diverge (one on the left and one on the right) or if one of them equals the current node, the current node is the LCA.
    # Detailed Steps in Pseudocode
  5. Initial Setup :
    • Start at the root node of the BST.
  6. Traversal Logic :
    • While the current node is not null:
      • Compare the values of \( p \), \( q \) with the current node's value.
      • Decide whether to move left, move right, or return the current node as the LCA.
  7. Return the Result :
    • The result is the node where the paths to \( p \) and \( q \) diverge or where one of the nodes equals the current node.
    # Pseudocode with Comments
                                                
    # Define the function to find the Lowest Common Ancestor in a BST
    function FindLowestCommonAncestor(root, node_p, node_q):
    # Start with the root of the tree
    current_node = root
    
    # Traverse the tree until we find the LCA or reach a null node
    while current_node is not null:
    # If both p and q are smaller than the current node's value, go left
    if node_p.value < current_node.value and node_q.value < current_node.value:
    current_node = current_node.left
    
    # If both p and q are greater than the current node's value, go right
    elif node_p.value > current_node.value and node_q.value > current_node.value:
    current_node = current_node.right
    
    # If p and q diverge or one of them equals the current node's value, return the current node as LCA
    else:
    return current_node
    
    # If we exit the loop without finding the LCA, return null (though per constraints, this should not happen)
    return null
    
                                            
    # Step-by-Step Explanation of the Pseudocode
  8. Initialization :
    • Assign the
      root
      of the tree to the variable
      current_node
      for traversal.
  9. Loop until LCA is Found or Reaching Null :
    • Check if both
      node_p
      and
      node_q
      have values less than
      current_node.value
      . If true, move to the left child (
      current_node.left
      ).
    • Check if both
      node_p
      and
      node_q
      have values greater than
      current_node.value
      . If true, move to the right child (
      current_node.right
      ).
    • If neither condition is met, then either:
      • One node lies on the left and the other on the right, making
        current_node
        the LCA.
      • One node equals
        current_node
        , making
        current_node
        the LCA as per the definition it allows the node to be a descendant of itself.
  10. Returning the Result :
    • The LCA is found at the point where the paths to
      node_p
      and
      node_q
      diverge, or at a point where one of the nodes is the
      current_node
      .
This approach ensures that the LCA is found efficiently by leveraging the properties of the BST, making the traversal nearly logarithmic in nature, i.e., \( O(\log n) \) on average, where \( n \) is the number of nodes in the tree.