Facebook Pixel

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:

  1. The tree follows in-order traversal sequence (left → root → right)
  2. The leftmost node (smallest value) becomes the new root
  3. Every node has no left child - all left pointers should be null
  4. Every node has at most one right child
  5. 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:

  1. We need to visit every node in the tree to rearrange them
  2. We need to follow in-order traversal (left → root → right), which is a specific type of DFS
  3. 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
  4. 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. Connect the previous node's right pointer to the current node
  2. Clear the current node's left pointer (set to null)
  3. 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:

  1. Base Case: If the current node is None, we simply return.

  2. Left Subtree: We first recursively process the entire left subtree with dfs(root.left). This ensures all smaller values are processed first.

  3. Current Node Processing: After the left subtree is done:

    • prev.right = root: Connect the previous node's right pointer to the current node
    • root.left = None: Remove the left child of the current node (as required)
    • prev = root: Update prev to point to the current node for the next iteration
  4. 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
  1. Start with dummy → root(2)
  2. Process left subtree: reach node 1
  3. Connect: dummy.right = 1, clear 1.left, prev = 1
  4. Back to node 2: Connect: 1.right = 2, clear 2.left, prev = 2
  5. Process right subtree: reach node 3
  6. 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 Evaluator

Example 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) → 12

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) → 123

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) → 1234

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) → 12345

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
Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

How many times is a tree node visited in a depth first search?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More