Before attempting this problem, you should be comfortable with:
Binary Search Tree (BST) Properties - Understanding that inorder traversal of a valid BST produces a sorted sequence
Inorder Traversal - Both recursive and iterative implementations for visiting nodes in left-root-right order
Tree Validation - Checking if a binary tree satisfies BST constraints using min/max bounds
Morris Traversal (Optional) - Space-optimized tree traversal technique using threading for O(1) space solution
1. Brute Force
Intuition
The simplest approach is to try every possible pair of nodes and check if swapping their values produces a valid BST. Since exactly two nodes were swapped, one such pair must restore the tree to its correct state.
For each pair of nodes, we swap their values, validate whether the resulting tree is a valid BST, and either keep the swap (if valid) or revert it (if invalid). While inefficient, this approach guarantees finding the solution by exhaustively checking all possibilities.
Algorithm
Define a helper function isBST that validates whether a tree is a valid BST using BFS with min/max bounds for each node.
Use a nested DFS approach: the outer DFS (dfs) iterates through each node as a potential first swap candidate.
For each first candidate, the inner DFS (dfs1) iterates through every other node as the second swap candidate.
For each pair, swap their values, check if the tree is now a valid BST, and if so, return true. Otherwise, swap back and continue.
Once a valid swap is found, the tree is corrected in place.
/**
* Definition for a binary tree node.
* public class TreeNode {
* public int val;
* public TreeNode left;
* public TreeNode right;
* public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/publicclassSolution{publicvoidRecoverTree(TreeNode root){Dfs(root, root);}privateboolDfs(TreeNode node1,TreeNode root){if(node1 ==null)returnfalse;if(Dfs1(node1, root, root))returntrue;returnDfs(node1.left, root)||Dfs(node1.right, root);}privateboolDfs1(TreeNode node1,TreeNode node2,TreeNode root){if(node2 ==null|| node1 == node2)returnfalse;Swap(node1, node2);if(IsBST(root))returntrue;Swap(node1, node2);returnDfs1(node1, node2.left, root)||Dfs1(node1, node2.right, root);}privateboolIsBST(TreeNode node){var q =newQueue<(TreeNode node,long min,long max)>();
q.Enqueue((node,long.MinValue,long.MaxValue));while(q.Count >0){var(cur, min, max)= q.Dequeue();if(cur ==null)continue;long val = cur.val;if(val <= min || val >= max)returnfalse;
q.Enqueue((cur.left, min, val));
q.Enqueue((cur.right, val, max));}returntrue;}privatevoidSwap(TreeNode a,TreeNode b){int temp = a.val;
a.val = b.val;
b.val = temp;}}
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/funcrecoverTree(root *TreeNode){var dfs func(node1 *TreeNode)boolvar dfs1 func(node1, node2 *TreeNode)bool
isBST :=func(node *TreeNode)bool{type item struct{
n *TreeNode
min, max int64}
q :=[]item{{node, math.MinInt64, math.MaxInt64}}forlen(q)>0{
cur := q[0]
q = q[1:]if cur.n ==nil{continue}
val :=int64(cur.n.Val)if val <= cur.min || val >= cur.max {returnfalse}
q =append(q, item{cur.n.Left, cur.min, val})
q =append(q, item{cur.n.Right, val, cur.max})}returntrue}
dfs1 =func(node1, node2 *TreeNode)bool{if node2 ==nil|| node1 == node2 {returnfalse}
node1.Val, node2.Val = node2.Val, node1.Val
ifisBST(root){returntrue}
node1.Val, node2.Val = node2.Val, node1.Val
returndfs1(node1, node2.Left)||dfs1(node1, node2.Right)}
dfs =func(node1 *TreeNode)bool{if node1 ==nil{returnfalse}ifdfs1(node1, root){returntrue}returndfs(node1.Left)||dfs(node1.Right)}dfs(root)}
/**
* Example:
* var ti = TreeNode(5)
* var v = ti.`val`
* Definition for a binary tree node.
* class TreeNode(var `val`: Int) {
* var left: TreeNode? = null
* var right: TreeNode? = null
* }
*/class Solution {funrecoverTree(root: TreeNode?): Unit {funisBST(node: TreeNode?): Boolean {val q = ArrayDeque<Triple<TreeNode?, Long, Long>>()
q.add(Triple(node, Long.MIN_VALUE, Long.MAX_VALUE))while(q.isNotEmpty()){val(cur, min, max)= q.removeFirst()if(cur ==null)continueval v = cur.`val`.toLong()if(v <= min || v >= max)returnfalse
q.add(Triple(cur.left, min, v))
q.add(Triple(cur.right, v, max))}returntrue}fundfs1(node1: TreeNode, node2: TreeNode?): Boolean {if(node2 ==null|| node1 === node2)returnfalseval tmp = node1.`val`
node1.`val` = node2.`val`
node2.`val` = tmp
if(isBST(root))returntrue
node2.`val` = node1.`val`
node1.`val` = tmp
returndfs1(node1, node2.left)||dfs1(node1, node2.right)}fundfs(node1: TreeNode?): Boolean {if(node1 ==null)returnfalseif(dfs1(node1, root))returntruereturndfs(node1.left)||dfs(node1.right)}dfs(root)}}
/**
* Definition for a binary tree node.
* public class TreeNode {
* public var val: Int
* public var left: TreeNode?
* public var right: TreeNode?
* public init() { self.val = 0; self.left = nil; self.right = nil; }
* public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; }
* public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) {
* self.val = val
* self.left = left
* self.right = right
* }
* }
*/classSolution{funcrecoverTree(_ root:TreeNode?){funcisBST(_ node:TreeNode?)->Bool{var q:[(TreeNode?,Int64,Int64)]=[(node,Int64.min,Int64.max)]while!q.isEmpty {let(cur, minVal, maxVal)= q.removeFirst()guardlet cur = cur else{continue}let v =Int64(cur.val)if v <= minVal || v >= maxVal {returnfalse}
q.append((cur.left, minVal, v))
q.append((cur.right, v, maxVal))}returntrue}funcdfs1(_ node1:TreeNode,_ node2:TreeNode?)->Bool{guardlet node2 = node2 else{returnfalse}if node1 === node2 {returnfalse}let tmp = node1.val
node1.val = node2.val
node2.val = tmp
ifisBST(root){returntrue}
node2.val = node1.val
node1.val = tmp
returndfs1(node1, node2.left)||dfs1(node1, node2.right)}funcdfs(_ node1:TreeNode?)->Bool{guardlet node1 = node1 else{returnfalse}ifdfs1(node1, root){returntrue}returndfs(node1.left)||dfs(node1.right)}_=dfs(root)}}
Time & Space Complexity
Time complexity: O(n2)
Space complexity: O(n)
2. Inorder Traversal
Intuition
A key property of BSTs is that an inorder traversal produces values in sorted order. When two nodes are swapped incorrectly, this sorted sequence will have one or two "inversions" where a value is greater than the next value.
If the swapped nodes are adjacent in the inorder sequence, there will be exactly one inversion. If they are not adjacent, there will be two inversions. In the first inversion, the larger (out-of-place) node is the first swapped node. In the second inversion (or the same one if only one exists), the smaller node is the second swapped node.
By collecting all nodes during inorder traversal and then scanning for these inversions, we can identify the two swapped nodes and swap their values back.
Algorithm
Perform an inorder traversal of the tree and store all nodes in a list.
Scan the list to find inversions (where arr[i].val > arr[i+1].val).
On the first inversion, mark node1 = arr[i] and node2 = arr[i+1].
If a second inversion is found, update node2 = arr[i+1] (the first swap target is already correct).
Swap the values of node1 and node2 to restore the BST.
/**
* Definition for a binary tree node.
* class TreeNode {
* constructor(val = 0, left = null, right = null) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/classSolution{/**
* @param {TreeNode} root
* @return {void} Do not return anything, modify root in-place instead.
*/recoverTree(root){const arr =[];constinorder=node=>{if(!node)return;inorder(node.left);
arr.push(node);inorder(node.right);};inorder(root);let node1 =null, node2 =null;for(let i =0; i < arr.length -1; i++){if(arr[i].val > arr[i +1].val){
node2 = arr[i +1];if(node1 ===null) node1 = arr[i];elsebreak;}}[node1.val, node2.val]=[node2.val, node1.val];}}
/**
* Definition for a binary tree node.
* public class TreeNode {
* public int val;
* public TreeNode left;
* public TreeNode right;
* public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/publicclassSolution{publicvoidRecoverTree(TreeNode root){var arr =newList<TreeNode>();Inorder(root, arr);TreeNode node1 =null, node2 =null;for(int i =0; i < arr.Count -1; i++){if(arr[i].val > arr[i +1].val){
node2 = arr[i +1];if(node1 ==null) node1 = arr[i];elsebreak;}}int temp = node1.val;
node1.val = node2.val;
node2.val = temp;}privatevoidInorder(TreeNode node,List<TreeNode> arr){if(node ==null)return;Inorder(node.left, arr);
arr.Add(node);Inorder(node.right, arr);}}
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/funcrecoverTree(root *TreeNode){var arr []*TreeNode
var inorder func(node *TreeNode)
inorder =func(node *TreeNode){if node ==nil{return}inorder(node.Left)
arr =append(arr, node)inorder(node.Right)}inorder(root)var node1, node2 *TreeNode
for i :=0; i <len(arr)-1; i++{if arr[i].Val > arr[i+1].Val {
node2 = arr[i+1]if node1 ==nil{
node1 = arr[i]}else{break}}}
node1.Val, node2.Val = node2.Val, node1.Val
}
/**
* Example:
* var ti = TreeNode(5)
* var v = ti.`val`
* Definition for a binary tree node.
* class TreeNode(var `val`: Int) {
* var left: TreeNode? = null
* var right: TreeNode? = null
* }
*/class Solution {funrecoverTree(root: TreeNode?): Unit {val arr = mutableListOf<TreeNode>()funinorder(node: TreeNode?){if(node ==null)returninorder(node.left)
arr.add(node)inorder(node.right)}inorder(root)var node1: TreeNode?=nullvar node2: TreeNode?=nullfor(i in0 until arr.size -1){if(arr[i].`val` > arr[i +1].`val`){
node2 = arr[i +1]if(node1 ==null) node1 = arr[i]elsebreak}}val temp = node1!!.`val`
node1.`val` = node2!!.`val`
node2.`val` = temp
}}
/**
* Definition for a binary tree node.
* public class TreeNode {
* public var val: Int
* public var left: TreeNode?
* public var right: TreeNode?
* public init() { self.val = 0; self.left = nil; self.right = nil; }
* public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; }
* public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) {
* self.val = val
* self.left = left
* self.right = right
* }
* }
*/classSolution{funcrecoverTree(_ root:TreeNode?){var arr =[TreeNode]()funcinorder(_ node:TreeNode?){guardlet node = node else{return}inorder(node.left)
arr.append(node)inorder(node.right)}inorder(root)var node1:TreeNode?=nilvar node2:TreeNode?=nilfor i in0..<(arr.count -1){if arr[i].val > arr[i +1].val {
node2 = arr[i +1]if node1 ==nil{ node1 = arr[i]}else{break}}}let temp = node1!.val
node1!.val = node2!.val
node2!.val = temp
}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(n)
3. Iterative Inorder Traversal
Intuition
This approach uses the same logic as the recursive inorder traversal but implements it iteratively using an explicit stack. The advantage is that we can detect inversions on the fly during traversal rather than collecting all nodes first.
By keeping track of the previously visited node, we can immediately detect when the current node's value is less than the previous node's value, signaling an inversion. This allows us to identify the swapped nodes in a single pass through the tree.
Algorithm
Initialize an empty stack and pointers for node1, node2, prev, and curr.
Traverse the tree iteratively using the stack for inorder traversal.
For each visited node, compare it with prev. If prev.val > curr.val, an inversion is found.
On the first inversion, set node1 = prev and node2 = curr.
On the second inversion, update node2 = curr and break early.
After traversal, swap the values of node1 and node2.
/**
* Definition for a binary tree node.
* public class TreeNode {
* public int val;
* public TreeNode left;
* public TreeNode right;
* public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/publicclassSolution{publicvoidRecoverTree(TreeNode root){var stack =newStack<TreeNode>();TreeNode node1 =null, node2 =null, prev =null, curr = root;while(stack.Count >0|| curr !=null){while(curr !=null){
stack.Push(curr);
curr = curr.left;}
curr = stack.Pop();if(prev !=null&& prev.val > curr.val){
node2 = curr;if(node1 ==null) node1 = prev;elsebreak;}
prev = curr;
curr = curr.right;}int temp = node1.val;
node1.val = node2.val;
node2.val = temp;}}
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/funcrecoverTree(root *TreeNode){var stack []*TreeNode
var node1, node2, prev *TreeNode
curr := root
forlen(stack)>0|| curr !=nil{for curr !=nil{
stack =append(stack, curr)
curr = curr.Left
}
curr = stack[len(stack)-1]
stack = stack[:len(stack)-1]if prev !=nil&& prev.Val > curr.Val {
node2 = curr
if node1 ==nil{
node1 = prev
}else{break}}
prev = curr
curr = curr.Right
}
node1.Val, node2.Val = node2.Val, node1.Val
}
/**
* Example:
* var ti = TreeNode(5)
* var v = ti.`val`
* Definition for a binary tree node.
* class TreeNode(var `val`: Int) {
* var left: TreeNode? = null
* var right: TreeNode? = null
* }
*/class Solution {funrecoverTree(root: TreeNode?): Unit {val stack = ArrayDeque<TreeNode>()var node1: TreeNode?=nullvar node2: TreeNode?=nullvar prev: TreeNode?=nullvar curr = root
while(stack.isNotEmpty()|| curr !=null){while(curr !=null){
stack.addLast(curr)
curr = curr.left
}
curr = stack.removeLast()if(prev !=null&& prev.`val` > curr!!.`val`){
node2 = curr
if(node1 ==null) node1 = prev
elsebreak}
prev = curr
curr = curr?.right
}val temp = node1!!.`val`
node1.`val` = node2!!.`val`
node2.`val` = temp
}}
/**
* Definition for a binary tree node.
* public class TreeNode {
* public var val: Int
* public var left: TreeNode?
* public var right: TreeNode?
* public init() { self.val = 0; self.left = nil; self.right = nil; }
* public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; }
* public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) {
* self.val = val
* self.left = left
* self.right = right
* }
* }
*/classSolution{funcrecoverTree(_ root:TreeNode?){var stack =[TreeNode]()var node1:TreeNode?=nilvar node2:TreeNode?=nilvar prev:TreeNode?=nilvar curr = root
while!stack.isEmpty || curr !=nil{while curr !=nil{
stack.append(curr!)
curr = curr!.left}
curr = stack.removeLast()if prev !=nil&& prev!.val > curr!.val {
node2 = curr
if node1 ==nil{ node1 = prev }else{break}}
prev = curr
curr = curr?.right}let temp = node1!.val
node1!.val = node2!.val
node2!.val = temp
}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(n)
4. Morris Traversal
Intuition
Morris traversal allows us to perform inorder traversal without using a stack or recursion, achieving O(1) space complexity. The technique works by temporarily modifying the tree structure: for each node with a left child, we find its inorder predecessor and create a temporary link back to the current node.
This temporary threading allows us to return to ancestor nodes after processing the left subtree without needing a stack. We can detect inversions during this traversal just like in the iterative approach, but without the extra space for a stack.
Algorithm
Initialize pointers for node1, node2, prev, and curr (starting at root).
While curr is not null:
If curr has no left child, process it (check for inversion with prev), update prev, and move to the right child.
If curr has a left child, find its inorder predecessor (rightmost node in left subtree).
If the predecessor's right pointer is null, create a thread to curr and move left.
If the predecessor's right pointer points to curr, remove the thread, process curr, update prev, and move right.
After traversal, swap the values of node1 and node2.
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/funcrecoverTree(root *TreeNode){var node1, node2, prev *TreeNode
curr := root
for curr !=nil{if curr.Left ==nil{if prev !=nil&& prev.Val > curr.Val {
node2 = curr
if node1 ==nil{
node1 = prev
}}
prev = curr
curr = curr.Right
}else{
pred := curr.Left
for pred.Right !=nil&& pred.Right != curr {
pred = pred.Right
}if pred.Right ==nil{
pred.Right = curr
curr = curr.Left
}else{
pred.Right =nilif prev !=nil&& prev.Val > curr.Val {
node2 = curr
if node1 ==nil{
node1 = prev
}}
prev = curr
curr = curr.Right
}}}
node1.Val, node2.Val = node2.Val, node1.Val
}
/**
* Example:
* var ti = TreeNode(5)
* var v = ti.`val`
* Definition for a binary tree node.
* class TreeNode(var `val`: Int) {
* var left: TreeNode? = null
* var right: TreeNode? = null
* }
*/class Solution {funrecoverTree(root: TreeNode?): Unit {var node1: TreeNode?=nullvar node2: TreeNode?=nullvar prev: TreeNode?=nullvar curr = root
while(curr !=null){if(curr.left ==null){if(prev !=null&& prev.`val` > curr.`val`){
node2 = curr
if(node1 ==null) node1 = prev
}
prev = curr
curr = curr.right
}else{var pred = curr.left
while(pred?.right !=null&& pred.right != curr){
pred = pred.right
}if(pred?.right ==null){
pred?.right = curr
curr = curr.left
}else{
pred.right =nullif(prev !=null&& prev.`val` > curr.`val`){
node2 = curr
if(node1 ==null) node1 = prev
}
prev = curr
curr = curr.right
}}}val temp = node1!!.`val`
node1.`val` = node2!!.`val`
node2.`val` = temp
}}
/**
* Definition for a binary tree node.
* public class TreeNode {
* public var val: Int
* public var left: TreeNode?
* public var right: TreeNode?
* public init() { self.val = 0; self.left = nil; self.right = nil; }
* public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; }
* public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) {
* self.val = val
* self.left = left
* self.right = right
* }
* }
*/classSolution{funcrecoverTree(_ root:TreeNode?){var node1:TreeNode?=nilvar node2:TreeNode?=nilvar prev:TreeNode?=nilvar curr = root
while curr !=nil{if curr!.left==nil{if prev !=nil&& prev!.val > curr!.val {
node2 = curr
if node1 ==nil{ node1 = prev }}
prev = curr
curr = curr!.right}else{var pred = curr!.leftwhile pred?.right!=nil&& pred?.right!== curr {
pred = pred?.right}if pred?.right==nil{
pred?.right= curr
curr = curr!.left}else{
pred?.right=nilif prev !=nil&& prev!.val > curr!.val {
node2 = curr
if node1 ==nil{ node1 = prev }}
prev = curr
curr = curr!.right}}}let temp = node1!.val
node1!.val = node2!.val
node2!.val = temp
}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(1)
Common Pitfalls
Assuming Only One Inversion Exists
When two nodes are swapped in a BST, there can be either one or two inversions in the inorder sequence depending on whether the swapped nodes are adjacent. Assuming only one inversion and not checking for a second one causes incorrect identification of the swapped nodes.
Swapping Node References Instead of Values
The problem asks to recover the tree by swapping values, not by restructuring node pointers. Attempting to swap the actual node positions in the tree is unnecessarily complex and error-prone. Simply swap the val fields of the two identified nodes.
Incorrectly Identifying First and Second Nodes
In the first inversion, node1 is the larger (out-of-place) element, and node2 is the smaller one. If a second inversion is found, node2 should be updated to the smaller element of that inversion, while node1 remains unchanged. Mixing up which node to update at each inversion leads to swapping the wrong pair.