Recover Binary Search Tree (Two Nodes Swapped)
Imagine a database index tree where two entries got swapped by mistake, causing queries to return incorrect results. Fixing the tree without rebuilding it from scratch is crucial for performance.
Given the root of a binary search tree (BST), where exactly two nodes have been swapped by mistake, recover the tree without changing its structure. Return nothing; modify the tree in-place.
The number of nodes in the tree is in the range [1, 10^5].Node values are unique.Exactly two nodes of the tree were swapped.You must solve the problem without changing the tree structure."root = [1,3,null,null,2]"[3,1,null,null,2]The nodes with values 1 and 3 are swapped. After recovery, the BST property is restored.
"root = [3,1,4,null,null,2]"[2,1,4,null,null,3]Nodes 2 and 3 are swapped. Fixing them restores the BST.
- Tree with only two nodes swapped at root and one child → should swap back
- Swapped nodes are adjacent in inorder traversal → simple swap
- Swapped nodes are far apart in inorder traversal → requires identifying two violations
- Tree with only one node → no change needed
Incorrect fix if only one violation detected, missing second swapped node
✅ Wait until traversal completes to swap values of both identified nodes
Only one violation detected, second node not updated properly
✅ Update second node on first violation and again if second violation occurs
Increased memory usage, losing O(1) space advantage
✅ Use Morris traversal or pointer tracking without arrays or stacks
Tree becomes corrupted after function ends
✅ Always revert temporary threads (pred.right) to null after use
Code may crash or swap wrong nodes
✅ Add null checks and handle trivial cases gracefully
Intuition
Perform an inorder traversal to get the node values in an array, sort the array to get the correct order, then assign the sorted values back to the nodes in inorder sequence.
Algorithm
- Perform inorder traversal and store node values in a list.
- Sort the list to get the correct node values order.
- Perform inorder traversal again, replacing node values with sorted values.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def recoverTree(root):
vals = []
def inorder(node):
if not node:
return
inorder(node.left)
vals.append(node.val)
inorder(node.right)
inorder(root)
sorted_vals = sorted(vals)
i = 0
def inorder_replace(node):
nonlocal i
if not node:
return
inorder_replace(node.left)
node.val = sorted_vals[i]
i += 1
inorder_replace(node.right)
inorder_replace(root)
# Example usage:
# root = TreeNode(1, TreeNode(3, None, TreeNode(2)))
# recoverTree(root)
# After calling recoverTree, the tree is fixed.vals = []Initialize list to store inorder valuesdef inorder(node):Define helper to traverse tree inordervals.append(node.val)Collect node values in inorder sequencesorted_vals = sorted(vals)Sort values to get correct BST orderdef inorder_replace(node):Helper to assign sorted values back to nodesnode.val = sorted_vals[i]Replace node value with correct sorted valuei += 1Move to next index in sorted valuesinorder_replace(root)Start second inorder traversal to replace valuesimport java.util.*;
class TreeNode {
int val;
TreeNode left, right;
TreeNode(int val) { this.val = val; }
}
public class Solution {
List<Integer> vals = new ArrayList<>();
int i = 0;
public void recoverTree(TreeNode root) {
inorder(root);
List<Integer> sortedVals = new ArrayList<>(vals);
Collections.sort(sortedVals);
i = 0;
inorderReplace(root, sortedVals);
}
private void inorder(TreeNode node) {
if (node == null) return;
inorder(node.left);
vals.add(node.val);
inorder(node.right);
}
private void inorderReplace(TreeNode node, List<Integer> sortedVals) {
if (node == null) return;
inorderReplace(node.left, sortedVals);
node.val = sortedVals.get(i++);
inorderReplace(node.right, sortedVals);
}
// Example main method
public static void main(String[] args) {
TreeNode root = new TreeNode(1);
root.left = new TreeNode(3);
root.left.right = new TreeNode(2);
new Solution().recoverTree(root);
// Tree is fixed after recoverTree
}
}List<Integer> vals = new ArrayList<>()Store inorder node valuesinorder(root)Traverse tree inorder to collect valuesCollections.sort(sortedVals)Sort values to restore BST ordernode.val = sortedVals.get(i++)Assign sorted value back to nodeif (node == null) returnBase case for recursion to avoid null pointeri = 0Reset index before second traversalinorderReplace(root, sortedVals)Start second inorder traversal to replace values#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct TreeNode {
int val;
TreeNode *left, *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution {
vector<int> vals;
int i = 0;
public:
void inorder(TreeNode* node) {
if (!node) return;
inorder(node->left);
vals.push_back(node->val);
inorder(node->right);
}
void inorderReplace(TreeNode* node, vector<int>& sortedVals) {
if (!node) return;
inorderReplace(node->left, sortedVals);
node->val = sortedVals[i++];
inorderReplace(node->right, sortedVals);
}
void recoverTree(TreeNode* root) {
inorder(root);
vector<int> sortedVals = vals;
sort(sortedVals.begin(), sortedVals.end());
i = 0;
inorderReplace(root, sortedVals);
}
};
// Example usage:
// int main() {
// TreeNode* root = new TreeNode(1);
// root->left = new TreeNode(3);
// root->left->right = new TreeNode(2);
// Solution().recoverTree(root);
// return 0;
// }vector<int> vals;Store inorder traversal valuesvoid inorder(TreeNode* node)Recursive inorder traversal to collect valuesvals.push_back(node->val);Add current node's value to listsort(sortedVals.begin(), sortedVals.end());Sort values to fix swapped nodesnode->val = sortedVals[i++];Assign sorted value back to nodeif (!node) return;Base case to stop recursioninorderReplace(root, sortedVals);Start second inorder traversal to replace valuesfunction TreeNode(val, left=null, right=null) {
this.val = val;
this.left = left;
this.right = right;
}
function recoverTree(root) {
const vals = [];
function inorder(node) {
if (!node) return;
inorder(node.left);
vals.push(node.val);
inorder(node.right);
}
inorder(root);
const sortedVals = [...vals].sort((a,b) => a-b);
let i = 0;
function inorderReplace(node) {
if (!node) return;
inorderReplace(node.left);
node.val = sortedVals[i++];
inorderReplace(node.right);
}
inorderReplace(root);
}
// Example usage:
// let root = new TreeNode(1, new TreeNode(3, null, new TreeNode(2)));
// recoverTree(root);
// Tree is fixed after recoverTreeconst vals = []Array to store inorder node valuesfunction inorder(node)Helper to traverse tree inordervals.push(node.val)Collect node values in inorder sequenceconst sortedVals = [...vals].sort((a,b) => a-b)Sort values to restore BST ordernode.val = sortedVals[i++]Replace node value with correct sorted valueif (!node) returnBase case to stop recursioninorderReplace(root)Start second inorder traversal to replace valuesO(n log n)O(n)Inorder traversal takes O(n), sorting takes O(n log n), and second traversal O(n). Overall dominated by sorting.
This approach works but is not optimal; it uses extra space and sorting, which can be improved.
Intuition
Since inorder traversal of BST is sorted, swapped nodes cause violations where a previous node's value is greater than the current node's value. Detect these violations to identify the two swapped nodes and swap their values back.
Algorithm
- Initialize pointers: prev (previous node), first and second (nodes to swap).
- Traverse the tree inorder, comparing current node value with prev node value.
- When a violation is found (prev.val > current.val), record nodes: first violation sets first and second, second violation updates second.
- After traversal, swap the values of first and second nodes.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def recoverTree(root):
first = second = prev = None
def inorder(node):
nonlocal first, second, prev
if not node:
return
inorder(node.left)
if prev and prev.val > node.val:
if not first:
first = prev
second = node
else:
second = node
prev = node
inorder(node.right)
inorder(root)
first.val, second.val = second.val, first.val
# Example usage:
# root = TreeNode(1, TreeNode(3, None, TreeNode(2)))
# recoverTree(root)
# Tree is fixed after recoverTreefirst = second = prev = NoneInitialize pointers to track swapped nodes and previous nodedef inorder(node):Recursive inorder traversal helperif prev and prev.val > node.val:Detect violation where BST property breaksif not first:First violation found, record first and second nodeselse:Second violation found, update second nodeprev = nodeUpdate prev to current node for next comparisonfirst.val, second.val = second.val, first.valSwap the values of the two nodes to fix BSTinorder(root)Start inorder traversal from rootclass TreeNode {
int val;
TreeNode left, right;
TreeNode(int val) { this.val = val; }
}
public class Solution {
TreeNode first = null, second = null, prev = null;
public void recoverTree(TreeNode root) {
inorder(root);
int temp = first.val;
first.val = second.val;
second.val = temp;
}
private void inorder(TreeNode node) {
if (node == null) return;
inorder(node.left);
if (prev != null && prev.val > node.val) {
if (first == null) {
first = prev;
second = node;
} else {
second = node;
}
}
prev = node;
inorder(node.right);
}
public static void main(String[] args) {
TreeNode root = new TreeNode(1);
root.left = new TreeNode(3);
root.left.right = new TreeNode(2);
new Solution().recoverTree(root);
}
}TreeNode first = null, second = null, prev = null;Pointers to track swapped nodes and previous nodeif (prev != null && prev.val > node.val)Detect violation in inorder traversalif (first == null)First violation found, record first and secondelseSecond violation found, update second nodeprev = node;Update prev for next comparisonint temp = first.val;Swap values of first and second nodesinorder(root);Start inorder traversal from root#include <iostream>
using namespace std;
struct TreeNode {
int val;
TreeNode *left, *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution {
TreeNode *first = nullptr, *second = nullptr, *prev = nullptr;
public:
void inorder(TreeNode* node) {
if (!node) return;
inorder(node->left);
if (prev && prev->val > node->val) {
if (!first) {
first = prev;
second = node;
} else {
second = node;
}
}
prev = node;
inorder(node->right);
}
void recoverTree(TreeNode* root) {
inorder(root);
swap(first->val, second->val);
}
};
// Example usage:
// int main() {
// TreeNode* root = new TreeNode(1);
// root->left = new TreeNode(3);
// root->left->right = new TreeNode(2);
// Solution().recoverTree(root);
// return 0;
// }TreeNode *first = nullptr, *second = nullptr, *prev = nullptr;Pointers to track swapped nodes and previous nodeif (prev && prev->val > node->val)Detect violation in inorder traversalif (!first)First violation found, record first and secondelseSecond violation found, update secondprev = node;Update prev for next comparisonswap(first->val, second->val);Swap values of the two nodes to fix BSTinorder(root);Start inorder traversal from rootfunction TreeNode(val, left=null, right=null) {
this.val = val;
this.left = left;
this.right = right;
}
function recoverTree(root) {
let first = null, second = null, prev = null;
function inorder(node) {
if (!node) return;
inorder(node.left);
if (prev && prev.val > node.val) {
if (!first) {
first = prev;
second = node;
} else {
second = node;
}
}
prev = node;
inorder(node.right);
}
inorder(root);
let temp = first.val;
first.val = second.val;
second.val = temp;
}
// Example usage:
// let root = new TreeNode(1, new TreeNode(3, null, new TreeNode(2)));
// recoverTree(root);
// Tree is fixed after recoverTreelet first = null, second = null, prev = null;Initialize pointers to track swapped nodes and previous nodeif (prev && prev.val > node.val)Detect violation in inorder traversalif (!first)First violation found, record first and secondelseSecond violation found, update secondprev = node;Update prev for next comparisonlet temp = first.val;Swap values of the two nodes to fix BSTinorder(root);Start inorder traversal from rootO(n)O(h)Single inorder traversal takes O(n) time. Space is O(h) due to recursion stack, where h is tree height.
This approach is the standard solution and expected in interviews for its efficiency and clarity.
Intuition
Morris traversal temporarily modifies tree links to traverse inorder without extra space. Using the same violation detection logic, we find swapped nodes and fix them in-place.
Algorithm
- Initialize pointers: first, second, prev, and current node.
- While current is not null, if current has no left child, process current and move right.
- If current has left child, find predecessor (rightmost node in left subtree).
- If predecessor's right is null, set it to current and move current to left child.
- If predecessor's right is current, revert it to null, process current, and move right.
- During processing, detect violations as in previous approach.
- After traversal, swap values of first and second nodes.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def recoverTree(root):
first = second = prev = None
current = root
while current:
if not current.left:
if prev and prev.val > current.val:
if not first:
first = prev
second = current
else:
second = current
prev = current
current = current.right
else:
pred = current.left
while pred.right and pred.right != current:
pred = pred.right
if not pred.right:
pred.right = current
current = current.left
else:
pred.right = None
if prev and prev.val > current.val:
if not first:
first = prev
second = current
else:
second = current
prev = current
current = current.right
first.val, second.val = second.val, first.val
# Example usage:
# root = TreeNode(1, TreeNode(3, None, TreeNode(2)))
# recoverTree(root)
# Tree is fixed after recoverTreecurrent = rootStart traversal from rootif not current.left:If no left child, process current node directlyif prev and prev.val > current.val:Detect violation in inorder sequencepred = current.leftFind predecessor in left subtreewhile pred.right and pred.right != current:Find rightmost node in left subtreepred.right = currentThread predecessor's right to current nodepred.right = NoneRemove temporary thread to restore treefirst.val, second.val = second.val, first.valSwap values of swapped nodesclass TreeNode {
int val;
TreeNode left, right;
TreeNode(int val) { this.val = val; }
}
public class Solution {
public void recoverTree(TreeNode root) {
TreeNode first = null, second = null, prev = null, current = root;
while (current != null) {
if (current.left == null) {
if (prev != null && prev.val > current.val) {
if (first == null) {
first = prev;
second = current;
} else {
second = current;
}
}
prev = current;
current = current.right;
} else {
TreeNode pred = current.left;
while (pred.right != null && pred.right != current) {
pred = pred.right;
}
if (pred.right == null) {
pred.right = current;
current = current.left;
} else {
pred.right = null;
if (prev != null && prev.val > current.val) {
if (first == null) {
first = prev;
second = current;
} else {
second = current;
}
}
prev = current;
current = current.right;
}
}
}
int temp = first.val;
first.val = second.val;
second.val = temp;
}
public static void main(String[] args) {
TreeNode root = new TreeNode(1);
root.left = new TreeNode(3);
root.left.right = new TreeNode(2);
new Solution().recoverTree(root);
}
}TreeNode first = null, second = null, prev = null, current = root;Initialize pointers for traversal and swapped nodesif (current.left == null)If no left child, process current nodewhile (pred.right != null && pred.right != current)Find predecessor in left subtreepred.right = current;Create temporary thread to current nodepred.right = null;Remove temporary thread to restore treeint temp = first.val;Swap values of the two swapped nodeswhile (current != null)Traverse until all nodes processed#include <iostream>
using namespace std;
struct TreeNode {
int val;
TreeNode *left, *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution {
public:
void recoverTree(TreeNode* root) {
TreeNode *first = nullptr, *second = nullptr, *prev = nullptr, *current = root;
while (current) {
if (!current->left) {
if (prev && prev->val > current->val) {
if (!first) {
first = prev;
second = current;
} else {
second = current;
}
}
prev = current;
current = current->right;
} else {
TreeNode* pred = current->left;
while (pred->right && pred->right != current) {
pred = pred->right;
}
if (!pred->right) {
pred->right = current;
current = current->left;
} else {
pred->right = nullptr;
if (prev && prev->val > current->val) {
if (!first) {
first = prev;
second = current;
} else {
second = current;
}
}
prev = current;
current = current->right;
}
}
}
swap(first->val, second->val);
}
};
// Example usage:
// int main() {
// TreeNode* root = new TreeNode(1);
// root->left = new TreeNode(3);
// root->left->right = new TreeNode(2);
// Solution().recoverTree(root);
// return 0;
// }TreeNode *first = nullptr, *second = nullptr, *prev = nullptr, *current = root;Initialize pointers for traversal and swapped nodesif (!current->left)If no left child, process current nodewhile (pred->right && pred->right != current)Find predecessor in left subtreepred->right = current;Create temporary thread to current nodepred->right = nullptr;Remove temporary thread to restore treeswap(first->val, second->val);Swap values of the two swapped nodeswhile (current)Traverse until all nodes processedfunction TreeNode(val, left=null, right=null) {
this.val = val;
this.left = left;
this.right = right;
}
function recoverTree(root) {
let first = null, second = null, prev = null, current = root;
while (current) {
if (!current.left) {
if (prev && prev.val > current.val) {
if (!first) {
first = prev;
second = current;
} else {
second = current;
}
}
prev = current;
current = current.right;
} else {
let pred = current.left;
while (pred.right && pred.right !== current) {
pred = pred.right;
}
if (!pred.right) {
pred.right = current;
current = current.left;
} else {
pred.right = null;
if (prev && prev.val > current.val) {
if (!first) {
first = prev;
second = current;
} else {
second = current;
}
}
prev = current;
current = current.right;
}
}
}
let temp = first.val;
first.val = second.val;
second.val = temp;
}
// Example usage:
// let root = new TreeNode(1, new TreeNode(3, null, new TreeNode(2)));
// recoverTree(root);
// Tree is fixed after recoverTreelet first = null, second = null, prev = null, current = root;Initialize pointers for traversal and swapped nodesif (!current.left)If no left child, process current nodewhile (pred.right && pred.right !== current)Find predecessor in left subtreepred.right = current;Create temporary thread to current nodepred.right = null;Remove temporary thread to restore treelet temp = first.val;Swap values of the two swapped nodeswhile (current)Traverse until all nodes processedO(n)O(1)Morris traversal visits each node at most twice, so O(n) time. Space is O(1) as no recursion or stack is used.
This is the most advanced solution, demonstrating mastery of tree traversal and in-place algorithms.
| Approach | Time | Space | Stack Risk | Reconstruct | Use In Interview |
|---|---|---|---|---|---|
| 1. Brute Force | O(n log n) | O(n) | No | Yes | Mention only - never code |
| 2. Better (Inorder Traversal with Two Pointers) | O(n) | O(h) | Yes (deep recursion) | Yes | Code this approach |
| 3. Optimal (Morris Inorder Traversal) | O(n) | O(1) | No | Yes | Mention as follow-up |
How to Present
Step 1: Clarify the problem and constraints with the interviewer.Step 2: Describe the brute force approach using inorder traversal and sorting.Step 3: Explain the better approach using inorder traversal with two pointers to detect swapped nodes.Step 4: If asked, discuss the Morris traversal approach for O(1) space.Step 5: Code the optimal approach and test with edge cases.
Time Allocation
What the Interviewer Tests
The interviewer tests your understanding of BST properties, inorder traversal, ability to detect subtle violations, and your skill in writing clean, efficient code with correct edge case handling.
Common Follow-ups
- What if more than two nodes are swapped? → Requires more complex detection or rebuilding.
- Can you do it without recursion or stack? → Use Morris traversal for O(1) space.
When to Use
1) Given a BST with exactly two nodes swapped, 2) Asked to fix the BST in-place, 3) Need to identify nodes violating inorder sorted order, 4) Optimize for time and space.
