You are given the root
of a binary search tree (BST), where the values of exactly two nodes of the tree were swapped by mistake. Recover the tree without changing its structure.
For example:
Input: root = [1,3,null,null,2]
Output: [3,1,null,null,2]
Explanation: 3 cannot be a left child of 1 because 3 > 1. Swapping 1 and 3 makes the BST valid.
Input: root = [3,1,4,null,null,2]
Output: [2,1,4,null,null,3]
Explanation: 2 cannot be in the right subtree of 3 because 2 < 3. Swapping 2 and 3 makes the BST valid.
Constraints:
[2, 1000]
.-2^31 <= Node.val <= 2^31 - 1
Follow up: A solution using O(n) space is pretty straight-forward. Could you devise a constant O(1) space solution?
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 goal is to fix a Binary Search Tree where two nodes are swapped accidentally. The brute force approach tries every possible pair of nodes in the tree, swaps them, and then checks if the resulting tree is a valid Binary Search Tree.
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
def recover_binary_search_tree_brute_force(root):
nodes = []
def inorder_traversal(node):
if node:
inorder_traversal(node.left)
nodes.append(node)
inorder_traversal(node.right)
inorder_traversal(root)
for first_node_index in range(len(nodes)):
for second_node_index in range(first_node_index + 1, len(nodes)):
first_node = nodes[first_node_index]
second_node = nodes[second_node_index]
# Temporarily swap the values of the two nodes
first_node.value, second_node.value = second_node.value, first_node.value
# After swapping, check if the tree is a valid BST
if is_valid_bst(root):
# If valid, swap back and return
first_node.value, second_node.value = second_node.value, first_node.value
return
# If not valid, swap back to continue the search
first_node.value, second_node.value = second_node.value, first_node.value
def is_valid_bst(root):
def validate(node, low=-float('inf'), high=float('inf')):
if not node:
return True
# Check if node's value is within the valid range
if node.value <= low or node.value >= high:
return False
# Recursively validate left and right subtrees with updated ranges
return validate(node.left, low, node.value) and validate(node.right, node.value, high)
return validate(root)
The key to efficiently fixing a messed-up binary search tree lies in identifying the incorrectly placed nodes. We can do this by observing the order in which we should see the nodes if the tree were correct and noting any violations. The goal is to find these out-of-order nodes and swap them.
Here's how the algorithm would work step-by-step:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def recoverTree(self, root):
self.first_incorrect_node = None
self.second_incorrect_node = None
self.previous_node = None
def inorder_traversal(node):
if not node:
return
inorder_traversal(node.left)
# Check for violation of BST property
if self.previous_node and node.val < self.previous_node.val:
# This is the first violation, mark nodes.
if not self.first_incorrect_node:
self.first_incorrect_node = self.previous_node
self.second_incorrect_node = node
else:
# Found the second violation.
self.second_incorrect_node = node
self.previous_node = node
inorder_traversal(node.right)
inorder_traversal(root)
# Swap the values of the incorrect nodes.
self.first_incorrect_node.val, self.second_incorrect_node.val = \
self.second_incorrect_node.val, self.first_incorrect_node.val
Case | How to Handle |
---|---|
Null or empty tree | Return the null root immediately as there's nothing to correct. |
Tree with only one node | Return the root as a single-node tree is inherently a valid BST. |
Tree with only two nodes, swapped | Directly compare and swap the values of the two nodes if they are out of order. |
Two adjacent nodes are swapped | The first and second violations in inorder traversal will directly point to the two swapped nodes. |
Two non-adjacent nodes are swapped | The first violation will identify the first node, and the second violation will identify the second node. |
Tree with duplicate values, including duplicates around the swapped nodes | The inorder traversal logic should still correctly identify the out-of-order nodes even with duplicates. |
Tree with negative values | The comparison logic works correctly with negative numbers in inorder traversal. |
Integer overflow if node values are very large (close to Integer.MAX_VALUE or Integer.MIN_VALUE) | Use long data type for comparison during inorder traversal to prevent potential overflows when calculating differences. |