897. Increasing Order Search Tree
Problem Description
You are given the root
of a binary search tree (BST). Your task is to rearrange the tree into a special form where:
- The tree follows in-order traversal sequence (left → root → right)
- The leftmost node (smallest value) becomes the new root
- Every node has no left child - all left pointers should be
null
- Every node has at most one right child
- The resulting structure forms a "right-skewed" tree that looks like a linked list
Essentially, you need to flatten the BST into a chain where nodes are connected only through their right pointers, and the nodes appear in ascending order (since it's a BST and we use in-order traversal).
For example, if you have a BST like:
5 / \ 3 6 / \ \ 2 4 8 / / \ 1 7 9
After rearrangement, it should become:
1 \ 2 \ 3 \ 4 \ 5 \ 6 \ 7 \ 8 \ 9
The values appear in sorted order (1→2→3→4→5→6→7→8→9), and each node only has a right child, forming a linear structure.
Flowchart Walkthrough
First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:
Is it a graph?
- Yes: While this is a binary search tree problem, a tree is a special type of graph (specifically, a connected acyclic graph). Trees have nodes and edges connecting parent-child relationships.
Is it a tree?
- Yes: The problem explicitly states we're working with a binary search tree (BST). We have a root node and each node has at most two children (left and right).
DFS
- Yes: Since we're dealing with a tree and need to traverse it, DFS (Depth-First Search) is the appropriate algorithm.
Conclusion: The flowchart leads us directly to using DFS for this problem.
This makes perfect sense because:
- We need to visit every node in the tree to rearrange them
- We need to follow in-order traversal (left → root → right), which is a specific type of DFS
- DFS allows us to recursively process the left subtree, then the current node, then the right subtree, which perfectly matches the in-order traversal pattern needed
- During the traversal, we can modify the tree structure by adjusting the left and right pointers to create the desired "right-skewed" linked list structure
The DFS approach with in-order traversal ensures we process nodes in ascending order (due to BST properties), allowing us to build the resulting structure correctly.
Intuition
The key insight comes from understanding what the problem is really asking: we need to transform a BST into a linked list where nodes are connected only through right pointers, and the values must be in ascending order.
Since we need ascending order and we're working with a BST, in-order traversal immediately comes to mind because it visits nodes in sorted order (left → root → right).
Now, how do we transform the tree while traversing? Think about building a linked list - we need to keep track of the "previous" node so we can connect it to the current node. This suggests we need a pointer that remembers the last node we processed.
Here's the clever part: instead of collecting all nodes first and then rebuilding the tree, we can modify the tree structure during the traversal itself. As we visit each node in in-order:
- Connect the previous node's right pointer to the current node
- Clear the current node's left pointer (set to
null
) - Update our "previous" pointer to the current node
But there's a small problem - what about the very first node? We don't have a "previous" node initially. This is where the dummy node technique comes in handy. We create a dummy node whose right child initially points to the root. This dummy node acts as our initial "previous" node, solving the edge case elegantly.
The beauty of this approach is that by the time we finish the in-order traversal, we've transformed the entire tree into the desired structure. The dummy node's right child now points to the new root (the smallest element), and all nodes are connected in a chain through their right pointers.
This solution is elegant because it leverages the natural order of in-order traversal and modifies the tree in-place during a single pass, achieving both the traversal and transformation simultaneously.
Learn more about Stack, Tree, Depth-First Search, Binary Search Tree and Binary Tree patterns.
Solution Approach
Let's walk through the implementation step by step, building on our DFS in-order traversal approach.
Initial Setup:
We create a dummy node and a prev
pointer, both initially pointing to the same dummy node. The dummy node's right child points to the original root. This setup handles the edge case of connecting the first (smallest) node.
dummy = prev = TreeNode(right=root)
The DFS Function: We define a recursive DFS function that performs in-order traversal:
def dfs(root):
if root is None:
return
nonlocal prev
dfs(root.left) # Process left subtree
# Process current node
prev.right = root
root.left = None
prev = root
dfs(root.right) # Process right subtree
How the Traversal Works:
-
Base Case: If the current node is
None
, we simply return. -
Left Subtree: We first recursively process the entire left subtree with
dfs(root.left)
. This ensures all smaller values are processed first. -
Current Node Processing: After the left subtree is done:
prev.right = root
: Connect the previous node's right pointer to the current noderoot.left = None
: Remove the left child of the current node (as required)prev = root
: Updateprev
to point to the current node for the next iteration
-
Right Subtree: Finally, process the right subtree with
dfs(root.right)
.
The Magic of nonlocal
:
The nonlocal prev
declaration allows the nested function to modify the prev
variable from the outer scope, maintaining state across recursive calls.
Example Walkthrough: For a simple BST:
2 / \ 1 3
- Start with dummy → root(2)
- Process left subtree: reach node 1
- Connect: dummy.right = 1, clear 1.left, prev = 1
- Back to node 2: Connect: 1.right = 2, clear 2.left, prev = 2
- Process right subtree: reach node 3
- Connect: 2.right = 3, clear 3.left, prev = 3
Result: dummy → 1 → 2 → 3
Return Value:
After the traversal completes, we return dummy.right
, which now points to the new root (the smallest element in the BST).
The time complexity is O(n)
as we visit each node once, and the space complexity is O(h)
where h
is the height of the tree, due to the recursion stack.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example to see how the solution transforms a BST step by step.
Initial BST:
4 / \ 2 5 / \ 1 3
Goal: Transform into a right-skewed tree: 1 → 2 → 3 → 4 → 5
Setup:
- Create
dummy
node with value 0 (arbitrary) - Set
prev = dummy
- Connect
dummy.right = root(4)
Initial state:
dummy(0) → 4 / \ 2 5 / \ 1 3
Step-by-step DFS Execution:
Step 1: dfs(4) called
- First, recursively call
dfs(2)
(left subtree)
Step 2: dfs(2) called
- First, recursively call
dfs(1)
(left subtree)
Step 3: dfs(1) called
dfs(None)
for left child - returns immediately- Process node 1:
prev.right = 1
(dummy now points to 1)1.left = None
(already None)prev = 1
(update prev pointer)
dfs(None)
for right child - returns immediately
Current structure:
dummy(0) → 1
Step 4: Back to dfs(2), process node 2
- Left subtree done
- Process node 2:
prev.right = 2
(node 1 now points to 2)2.left = None
(disconnect from node 1)prev = 2
(update prev pointer)
- Call
dfs(3)
for right subtree
Current structure:
dummy(0) → 1 → 2
Step 5: dfs(3) called
dfs(None)
for left child - returns immediately- Process node 3:
prev.right = 3
(node 2 now points to 3)3.left = None
(already None)prev = 3
(update prev pointer)
dfs(None)
for right child - returns immediately
Current structure:
dummy(0) → 1 → 2 → 3
Step 6: Back to dfs(4), process node 4
- Left subtree done
- Process node 4:
prev.right = 4
(node 3 now points to 4)4.left = None
(disconnect from node 2)prev = 4
(update prev pointer)
- Call
dfs(5)
for right subtree
Current structure:
dummy(0) → 1 → 2 → 3 → 4
Step 7: dfs(5) called
dfs(None)
for left child - returns immediately- Process node 5:
prev.right = 5
(node 4 now points to 5)5.left = None
(already None)prev = 5
(update prev pointer)
dfs(None)
for right child - returns immediately
Final structure:
dummy(0) → 1 → 2 → 3 → 4 → 5
Return: dummy.right
which is node 1, the new root of our transformed tree.
The key insight is that in-order traversal naturally visits nodes in ascending order for a BST. By maintaining a prev
pointer and connecting nodes as we visit them, we build the right-skewed tree incrementally. The dummy node elegantly handles the edge case of connecting to the first (smallest) node.
Solution Implementation
1# Definition for a binary tree node.
2# class TreeNode:
3# def __init__(self, val=0, left=None, right=None):
4# self.val = val
5# self.left = left
6# self.right = right
7
8class Solution:
9 def increasingBST(self, root: TreeNode) -> TreeNode:
10 """
11 Rearranges a Binary Search Tree into a right-skewed tree where each node
12 has no left child and only one right child, maintaining the original BST order.
13
14 Args:
15 root: The root node of the binary search tree
16
17 Returns:
18 The root of the rearranged tree
19 """
20
21 def inorder_traverse(node: TreeNode) -> None:
22 """
23 Performs in-order traversal of the BST and rearranges nodes
24 to form a right-skewed tree.
25
26 Args:
27 node: Current node being processed
28 """
29 # Base case: if node is None, return
30 if node is None:
31 return
32
33 # Use nonlocal to modify the prev variable from outer scope
34 nonlocal prev
35
36 # Process left subtree first (in-order traversal)
37 inorder_traverse(node.left)
38
39 # Connect previous node's right pointer to current node
40 prev.right = node
41 # Remove left pointer of current node (no left children in result)
42 node.left = None
43 # Update prev to current node for next iteration
44 prev = node
45
46 # Process right subtree
47 inorder_traverse(node.right)
48
49 # Create a dummy node to simplify edge cases
50 # prev will track the last processed node during traversal
51 dummy = prev = TreeNode(right=root)
52
53 # Perform the in-order traversal and rearrangement
54 inorder_traverse(root)
55
56 # Return the actual root (skip dummy node)
57 return dummy.right
58
1/**
2 * Definition for a binary tree node.
3 * public class TreeNode {
4 * int val;
5 * TreeNode left;
6 * TreeNode right;
7 * TreeNode() {}
8 * TreeNode(int val) { this.val = val; }
9 * TreeNode(int val, TreeNode left, TreeNode right) {
10 * this.val = val;
11 * this.left = left;
12 * this.right = right;
13 * }
14 * }
15 */
16class Solution {
17 // Instance variable to track the previous node during in-order traversal
18 private TreeNode previousNode;
19
20 /**
21 * Transforms a Binary Search Tree into an increasing order tree
22 * where each node only has a right child pointing to the next node in order.
23 *
24 * @param root The root of the binary search tree
25 * @return The root of the transformed increasing order tree
26 */
27 public TreeNode increasingBST(TreeNode root) {
28 // Create a dummy node to simplify edge cases
29 // The actual result will be dummy.right
30 TreeNode dummyNode = new TreeNode(0, null, root);
31
32 // Initialize previousNode to the dummy node
33 previousNode = dummyNode;
34
35 // Perform in-order traversal to reorganize the tree
36 performInOrderTraversal(root);
37
38 // Return the right child of dummy node, which is the new root
39 return dummyNode.right;
40 }
41
42 /**
43 * Performs in-order traversal of the BST and reorganizes nodes
44 * to form a right-skewed tree with increasing values.
45 *
46 * @param currentNode The current node being processed
47 */
48 private void performInOrderTraversal(TreeNode currentNode) {
49 // Base case: if current node is null, return
50 if (currentNode == null) {
51 return;
52 }
53
54 // Process left subtree first (in-order traversal)
55 performInOrderTraversal(currentNode.left);
56
57 // Process current node:
58 // Link the previous node's right pointer to current node
59 previousNode.right = currentNode;
60
61 // Clear current node's left pointer
62 currentNode.left = null;
63
64 // Update previousNode to current node for next iteration
65 previousNode = currentNode;
66
67 // Process right subtree
68 performInOrderTraversal(currentNode.right);
69 }
70}
71
1/**
2 * Definition for a binary tree node.
3 * struct TreeNode {
4 * int val;
5 * TreeNode *left;
6 * TreeNode *right;
7 * TreeNode() : val(0), left(nullptr), right(nullptr) {}
8 * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
9 * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
10 * };
11 */
12class Solution {
13public:
14 /**
15 * Rearranges the given BST into a right-skewed tree (increasing order)
16 * where each node has no left child and only one right child.
17 *
18 * @param root The root of the binary search tree
19 * @return The root of the rearranged tree
20 */
21 TreeNode* increasingBST(TreeNode* root) {
22 // Create a dummy node to simplify edge cases
23 // The actual result will be dummy->right
24 TreeNode* dummyNode = new TreeNode(0, nullptr, root);
25
26 // Keep track of the previous node during in-order traversal
27 TreeNode* previousNode = dummyNode;
28
29 // Define recursive function for in-order traversal
30 // Using lambda function with capture by reference
31 function<void(TreeNode*)> inorderTraversal = [&](TreeNode* currentNode) {
32 // Base case: if current node is null, return
33 if (!currentNode) {
34 return;
35 }
36
37 // Process left subtree first (in-order traversal)
38 inorderTraversal(currentNode->left);
39
40 // Process current node:
41 // 1. Link previous node's right pointer to current node
42 previousNode->right = currentNode;
43 // 2. Remove left child of current node
44 currentNode->left = nullptr;
45 // 3. Update previous node to current node
46 previousNode = currentNode;
47
48 // Process right subtree
49 inorderTraversal(currentNode->right);
50 };
51
52 // Start the in-order traversal from root
53 inorderTraversal(root);
54
55 // Return the actual root (skip dummy node)
56 return dummyNode->right;
57 }
58};
59
1/**
2 * Definition for a binary tree node.
3 * class TreeNode {
4 * val: number
5 * left: TreeNode | null
6 * right: TreeNode | null
7 * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
8 * this.val = (val===undefined ? 0 : val)
9 * this.left = (left===undefined ? null : left)
10 * this.right = (right===undefined ? null : right)
11 * }
12 * }
13 */
14
15/**
16 * Transforms a Binary Search Tree into an increasing order tree
17 * where each node only has a right child pointing to the next node in order
18 * @param root - The root of the binary search tree
19 * @returns The root of the restructured tree with only right children
20 */
21function increasingBST(root: TreeNode | null): TreeNode | null {
22 // Create a dummy node to simplify edge cases
23 // This dummy node's right child will eventually point to the new tree's root
24 const dummyNode: TreeNode = new TreeNode(0);
25
26 // Keep track of the previous node during in-order traversal
27 let previousNode: TreeNode = dummyNode;
28
29 /**
30 * Performs in-order traversal and restructures the tree
31 * @param currentNode - The current node being processed
32 */
33 const performInOrderTraversal = (currentNode: TreeNode | null): void => {
34 // Base case: if current node is null, return
35 if (!currentNode) {
36 return;
37 }
38
39 // Process left subtree first (in-order traversal)
40 performInOrderTraversal(currentNode.left);
41
42 // Process current node:
43 // Connect previous node's right pointer to current node
44 previousNode.right = currentNode;
45 // Remove left child to maintain increasing order structure
46 currentNode.left = null;
47 // Update previous node for next iteration
48 previousNode = currentNode;
49
50 // Process right subtree last
51 performInOrderTraversal(currentNode.right);
52 };
53
54 // Start the in-order traversal from root
55 performInOrderTraversal(root);
56
57 // Return the actual root (skip the dummy node)
58 return dummyNode.right;
59}
60
Time and Space Complexity
Time Complexity: O(n)
The algorithm performs an in-order traversal of the binary search tree using DFS (Depth-First Search). During this traversal, each node is visited exactly once. For each node visit, the operations performed are:
- Checking if the node is
None
:O(1)
- Recursively calling
dfs
on left and right children - Reassigning pointers (
prev.right = root
,root.left = None
,prev = root
):O(1)
Since we visit all n
nodes exactly once and perform constant-time operations at each node, the total time complexity is O(n)
.
Space Complexity: O(n)
The space complexity comes from the recursive call stack used during the DFS traversal. In the worst case, when the tree is completely unbalanced (essentially a linked list), the recursion depth can be O(n)
. In the best case of a balanced tree, the recursion depth would be O(log n)
. However, considering the worst-case scenario, the space complexity is O(n)
.
Additionally, the algorithm modifies the tree in-place and only uses a constant amount of extra space for the dummy
and prev
variables, which doesn't affect the overall space complexity dominated by the recursion stack.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Forgetting to Clear Left Pointers
The Problem:
A common mistake is connecting nodes correctly through their right pointers but forgetting to set node.left = None
. This results in a tree that still has left children, violating the requirement that all nodes should have no left child.
Incorrect Code:
def inorder_traverse(node):
if node is None:
return
nonlocal prev
inorder_traverse(node.left)
prev.right = node
# FORGOT: node.left = None ← Missing this line!
prev = node
inorder_traverse(node.right)
Why It Happens: When focusing on building the right-skewed chain, it's easy to overlook that the original left pointers are still intact. The nodes get connected correctly via right pointers, but the old left connections remain.
The Fix:
Always remember to explicitly set node.left = None
after connecting the node to the chain:
prev.right = node node.left = None # Critical: Clear the left pointer prev = node
Pitfall 2: Not Using nonlocal
for the prev
Variable
The Problem:
Without the nonlocal
declaration, the inner function creates a local prev
variable instead of modifying the outer scope's prev
. This breaks the chain-building logic entirely.
Incorrect Code:
def increasingBST(self, root):
dummy = prev = TreeNode(right=root)
def inorder_traverse(node):
if node is None:
return
# FORGOT: nonlocal prev ← Missing this line!
inorder_traverse(node.left)
prev.right = node # This creates a local 'prev' or throws an error
node.left = None
prev = node
inorder_traverse(node.right)
inorder_traverse(root)
return dummy.right
Why It Happens:
Python's scoping rules require explicit declaration when modifying variables from an enclosing scope. Without nonlocal
, Python either creates a new local variable or throws an UnboundLocalError
.
The Fix:
Always declare nonlocal prev
at the beginning of the inner function:
def inorder_traverse(node):
if node is None:
return
nonlocal prev # Essential for modifying outer scope variable
# ... rest of the code
Pitfall 3: Incorrect Dummy Node Initialization
The Problem:
Creating the dummy node incorrectly or forgetting to initialize prev
to point to it can cause the first node to be disconnected from the result.
Incorrect Code:
# Wrong: Separate initialization dummy = TreeNode() prev = TreeNode() # Different node! dummy.right = root # Or forgetting to set dummy.right dummy = prev = TreeNode() # No connection to root
Why It Happens:
The dummy node pattern requires careful setup. The dummy and prev
must initially reference the same node, and the dummy should not initially point to the root (since we'll rebuild the connections).
The Fix: Use a single line initialization that creates one dummy node and assigns it to both variables:
dummy = prev = TreeNode() # Both point to same dummy node # Don't set dummy.right = root here; let the traversal handle it
Complete Corrected Solution:
class Solution:
def increasingBST(self, root: TreeNode) -> TreeNode:
def inorder_traverse(node):
if node is None:
return
nonlocal prev # Don't forget this!
inorder_traverse(node.left)
prev.right = node
node.left = None # Don't forget to clear left pointer!
prev = node
inorder_traverse(node.right)
dummy = prev = TreeNode() # Correct initialization
inorder_traverse(root)
return dummy.right
How many times is a tree node visited in a depth first search?
Recommended Readings
Stack Intro Imagine you have a pile of books on your desk If you want to add a new book you place it on top If you want to read a book you take it from the top And if you simply want to see which book is on the top you
Everything About Trees A tree is a type of graph data structure composed of nodes and edges Its main properties are It is acyclic doesn't contain any cycles There exists a path from the root to any node Has N 1 edges where N is the number of nodes in the tree and
https assets algo monster cover_photos dfs svg Depth First Search Prereqs Recursion Review problems recursion_intro Trees problems tree_intro With a solid understanding of recursion under our belts we are now ready to tackle one of the most useful techniques in coding interviews Depth First Search DFS As the name suggests
Want a Structured Path to Master System Design Too? Don’t Miss This!