Given the root
of a binary search tree (BST) and a node p
in the tree, find the in-order successor of the node p
in the BST. If the in-order successor does not exist, return null
.
The successor of a node p
is the node with the smallest key greater than p.val
.
Example 1:
Input: root = [5,3,6,2,4,null,null,1], p = 6
Output: null
Explanation: There is no in-order successor of the current node, so the answer is null
.
Example 2:
Input: root = [5,3,6,2,4,null,null,1], p = 1 Output: 2 Explanation: 1's in-order successor node is 2. Note that the successor could be a descendant of the node.
Example 3:
Input: root = [2,1,3], p = 1 Output: 2
Constraints:
[1, 104]
.-105 <= Node.val <= 105
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force way to find the inorder successor is like exhaustively searching through every single option. We essentially convert the tree into a sorted list and then find the next element after the target node's value. This guarantees we will find the successor if it exists.
Here's how the algorithm would work step-by-step:
def inorder_successor_brute_force(root, node):
def inorder_traversal(current_node, sorted_list):
if current_node is None:
return
inorder_traversal(current_node.left, sorted_list)
sorted_list.append(current_node)
inorder_traversal(current_node.right, sorted_list)
sorted_nodes = []
# We need to flatten the tree to find the inorder successor.
inorder_traversal(root, sorted_nodes)
node_found = False
for index, current_node in enumerate(sorted_nodes):
if current_node == node:
node_found = True
# The next node in the sorted list is the successor.
if index + 1 < len(sorted_nodes):
return sorted_nodes[index + 1]
# If we reach here, there is no inorder successor
return None
The goal is to find the node that comes right after a given node in a binary search tree when you visit the nodes in increasing order. We can find this 'next' node efficiently by cleverly navigating the tree, focusing on the right subtree or the path back up to the root.
Here's how the algorithm would work step-by-step:
class TreeNode:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
self.parent = None
def find_inorder_successor(node):
if node.right:
# Node has right child, find leftmost in right subtree.
return find_leftmost_node(node.right)
# Node has no right child, find the ancestor.
return find_ancestor_successor(node)
def find_leftmost_node(node):
current_node = node
while current_node.left:
current_node = current_node.left
return current_node
def find_ancestor_successor(node):
current_node = node
while current_node.parent:
parent_node = current_node.parent
#Check if current node is in the left subtree of parent
if parent_node.left == current_node:
return parent_node
current_node = parent_node
# Node is the rightmost node, no inorder successor
return None
Case | How to Handle |
---|---|
Root is null | Return null immediately since an empty tree has no successor. |
Node p is null | Return null immediately since a null node has no successor. |
Node p is the rightmost node in the tree (no successor exists) | The algorithm should return null when no node is greater than p. |
Node p has a right subtree | The inorder successor is the smallest node in p's right subtree; locate using a left-most descent from the right child. |
Node p does not have a right subtree, and it's somewhere in the middle of the tree | The algorithm should traverse up the tree to find the first ancestor that p is in its left subtree. |
Tree with only one node (root) and p is the root | Return null since the root is the only node, and thus has no successor. |
BST with skewed structure (e.g., all nodes are left children) | The algorithm should still correctly find the successor by traversing up the skewed tree. |
Duplicate values in the BST | The algorithm should still correctly identify the inorder successor based on the *smallest* value greater than p.val. |