### Introduction#

Daniel (🧔🏽‍♂️) nodded his head slowly as he added cream to his freshly poured coffee. He had worked hard to learn his Leetcode and he was feeling ready. He took a sip and walked over to his laptop and sat down. Moments later the interview began.

👩 “Good afternoon Daniel! Thanks for taking the time to interview with me today. Let’s begin with some brief introductions. My name is Jordanna! I’m a senior engineer on the user performance improvement team and I will be conducting your coding interview today.”

🧔🏽‍♂️ “Thank you for this opportunity, I’m looking forward to it. I graduated with a B.Sc. in civil engineering from UTSA two years ago and I’ve spent the last year and a half working DuBank as a junior software engineer.”

👩 “It’s a pleasure to meet you Daniel. Let’s dive into this.” Jordanna (👩 ) copied the problem into the shared code editor and Daniel reads the problem statement as he takes a slow sip of his refreshing medium roast.

### Problem Statement#

Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree.

According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes p and q as the lowest node in T that has both p and q as descendants (where we allow a node to be a descendant of itself).”

#### Example 1:#

``````                3

5                       1

6       2               0       8

7       4

Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
Output: 3
Explanation: The LCA of nodes 5 and 1 is 3.
``````

#### Example 2:#

``````                3

5                       1

6       2               0       8

7       4

Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
Output: 5
Explanation: The LCA of nodes 5 and 4 is 5,
since a node can be a descendant of itself
according to the LCA definition.
``````

#### Example 3:#

``````Input: root = [1,2], p = 1, q = 2
Output: 1
``````

🧔🏽‍♂️ “We are given a binary tree and we are asked to find the lowest common ancestor. If I understand correctly, this is not a binary search tree, am I correct?”

👩 “Yes, as we can see in the examples, the nodes are not sorted.”

🧔🏽‍♂️ “What is the range on the number of nodes that we can expect to see?”

👩 “We can expect anywhere from `2` to `10,000` nodes.”

🧔🏽‍♂️ ”Are all node values unique?”

👩 “Yes, all node values are unique.”

🧔🏽‍♂️ “Can we ever have a case where `p` and `q` are equal?”

👩 “No, `p` will never be equal to `q`.”

🧔🏽‍♂️ “Ok, thanks! That helps. If we look at the root node of the tree, there are three different case that we need to account for. One: both nodes `p` and `q` could be in there left subtree, Two: one node could be in the left subtree and the other could be in the right subtree. Three: both nodes could be in the right subtree. Would you say that I have accurately described the different scenarios?”

👩 “I like where you are going with this, Daniel! It’s a good start. Are there any other cases that you can think of?”

🧔🏽‍♂️ “Let me scan the problem statement again… Ah, yes! The LCA could be one of the nodes. We could have a case where `p` is a direct ancestor of `q` or vice versa. I believe this expands upon cases one and three. Would you say that my articulation of the problem is correct? ”

👩 “Yes, it sounds like you’re ready to start coming up with a solution. How would you write this up?””

🧔🏽‍♂️ “Thanks Jordanna, I’d love to! Since this is a tree traversal problem, it can be solved using recursion. Since I’ll be using Python, I’ll need to be mindful of the recursion limit. By default, the recursion limit in Python is typically set to `1,000`. I’ll begin by setting it to `20,000` at the beginning of my solution and I’ll change it back at the end. Are you ok with this approach?”

👩 “Yes, so far so good. How would you write the recursive algorithm?”

🧔🏽‍♂️ “At every node, we will recursively traverse down the left subtree and then down the right. When we reach the leftmost leaf node, the next left subtree recursive call will return `None` and the next right subtree recursive call will return `None`. We can use `left` and `right` variables to keep track of these returned values.”

👩 Jordanna nodded her head in agreement.

🧔🏽‍♂️ “If the `root` value equals that of `p` or `q`, we return the root address up the to the next stack frame. If `left` and `right` are not `None`, we have found our `LCA` and return root. At this point in the `else if`, we only have two options remaining. If `left` is not `None`, we return `left`, otherwise we return `right`.” Daniel paused for a moment, and continued. “If `p` or `q` was found in the `right` subtree, `right` will equal the node’s memory address, otherwise, we will return `None`. As this algorithm runs it course, the `LCA` node address will be returned all of the way up to the top stack frame until it is return to the original caller.”

### Recursive Solution (p & q exist)#

``````# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
"""
Time: O(N)
Space: O(N)
"""
if not root:
return None

# Recursively search through the left and then the right branches
left  = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p, q)

# return the answer ~ last call
if root.val == p.val or root.val == q.val:
# Return the node when we find it
return root
elif left and right:
# If left and right are not None, we have found
# the LCA. Return it up to the next stack frame.
return root
elif left:
# Since p or q was found in the left subtree,
# return it up the call stack.
return left
else:
# if p or q was found in the right subtree,
# it will be returned up, if neither were
# found, we will return None. This means that
# yet.
return right
``````

### Recursive solution (p or q may not exist)#

👩 “Daniel this looking good! As a follow-up. Let’s say that we no longer can guarantee that both `p` and `q` exist in the binary tree. How would this change your solution?”

🧔🏽‍♂️ “Ok, let’s see…” Daniel took a moment and reviewed his solution and continued. “If we don’t have two target nodes in the binary tree, my current solution will return the memory address of the one node we did find. I can see how this could cause problems. If a valid `LCA` does not exist in the tree, I would expect a `None` response. I believe we will need to keep track of the number nodes that have been found. Am I on the right track?”

👩 “Yes, you’re on the right track. Please continue.”

🧔🏽‍♂️ “I’m thinking we can traverse through the binary tree in the same way we did in the original solution. Only this time we’ll use a `traverse(...,ans:TreeNode=None)` function with a default parameter to keep track of the `LCA` node. This function will return a (`flag`, `ans`) tuple. We can use `left` and `right` variables to keep track of the `left` and `right` subtree search result flags. If we find `p` or `q` at a node, we will return a tuple with the flag and `None` (`1`, `None`). After we traverse the `left` and `right` subtrees for each node, we can set `flag = 0` for that node and then check if we have found the `LCA`. Jordanna, do you have any comments or concerns with where I’m going with this?”

👩 “So far so good Daniel.”

🧔🏽‍♂️ “Ok, if the current `node` is equal to either `p` or `q`, we will set `flag = 1`. After that, we can sum up the flags and see if we have found the `LCA` yet. If `(left = 1) or (right = 1)` and `flag = 1`, we will set `ans = node`. If `(left = 1) and (right = 1)` and `flag = 0`, we will set `ans = node`. Once we have found the `LCA`, `left + flag + right` will equal `1` and `ans` will equal the `LCA` `node` for the remainder of the recursive calls. The `ans` will be returned through the call stack up to the top and returned to the original caller. If we only ever find `1` target node, we will never set the `ans` to a node memory address and will eventually return `None` as our final answer."

👩 “We still have some time left here, can you please code it up for me?”

🧔🏽‍♂️ “For sure!" Daniel responded cheerfully and hammered out the solution below."

``````# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

from typing import Tuple

class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
def traverse(node: TreeNode, p: TreeNode, q:TreeNode, ans: TreeNode = None) -> Tuple[Any]:
"""
Time: O(N)
Space: O(N)
"""
if not node:
return 0, ans

# Post order traversal through binary tree
left, ans = traverse(node.left, p, q, ans = ans)
right, ans = traverse(node.right, p, q, ans = ans)

flag = 0

# Set flag = 1 if p or q found
if node.val == p.val or node.val == q.val:
flag = 1

# The sum of the flags will be greater
# than 1 if p & q have been found
if left + flag + right == 2:
ans = node

# If p or q found in a subtree or
# the current node, return 1
if left or flag or right:
return 1, ans
else:
return 0, ans

_, ans = traverse(root, p, q)

return ans
``````

👩 “Can you run through an example for me?”

🧔🏽‍♂️ “Are you ok with me pointing my camera towards my sheet of paper here so that I can draw a tree and run through an example?”

👩 “Yes, for sure!”

🧔🏽‍♂️ Daniel worked through an example and validated his solution.

👩 After reviewing Daniel’s solution, Jordanna closed the meeting: “Thanks for taking the time interview with me today!”

🧔🏽‍♂️ “Thanks Jordanna, have a great afternoon!”

👩 “You’re welcome Daniel! You too!”

Leetcode 236 Lowest Common Ancestor of a Binary Tree