Start learning free

LESSON 345 OF 441 · BINARY TREES

Kth Smallest Element in a BST in C#: Iterative In-Order, O(h + k)

Practice O(n) Read the full explanation

Given the root of a binary search tree (BST) and an integer k, return the k-th smallest value among all the values in the tree. k counts from 1: k = 1 asks for the smallest value.

In a BST, every value in a node's left subtree is smaller than the node's value, and every value in its right subtree is larger.

The trees below are written in level order (top to bottom, left to right), with null for a missing child.

 

Example 1:

    3
   / \
  1   4
   \
    2

Input: root = [3,1,4,null,2], k = 1
Output: 1
Explanation: In increasing order the values are 1, 2, 3, 4. The 1st smallest is 1.

Example 2:

        5
       / \
      3   6
     / \
    2   4
   /
  1

Input: root = [5,3,6,2,4,null,null,1], k = 3
Output: 3
Explanation: In increasing order the values are 1, 2, 3, 4, 5, 6. The 3rd smallest is 3.

Example 3:

Input: root = [5,3,6,2,4,null,null,1], k = 6
Output: 6
Explanation: The tree has 6 nodes, so the 6th smallest value is the largest one.

 

Constraints:

  • The number of nodes in the tree, n, is in the range [1, 1000].
  • Each node value is between -104 and 104, and all values are different.
  • 1 <= k <= n

 

Follow up: Can you stop as soon as you reach the answer, in O(h + k) time, where h is the height of the tree?
STEP 1 OF 12
    Ctrl + Enter to check

    C# · BINARY TREES

    Kth Smallest Element In Bst in C#, explained

    Problem overview

    The method KthSmallest receives the root of a binary search tree of TreeNode objects and an integer k. It returns the k-th smallest value in the tree, counting from 1, so k = 1 asks for the smallest value. All values are different, and k is between 1 and the number of nodes.

    For the tree with root 3, left child 1 (which has a right child 2) and right child 4, the values in increasing order are 1, 2, 3, 4. With k = 1 the answer is 1. For the tree with root 5, children 3 and 6, then 2 and 4 under 3, and 1 under 2, the sorted values are 1, 2, 3, 4, 5, 6, so k = 3 gives 3 and k = 6 gives 6.

    Approach: in-order traversal that stops at the k-th node

    In a binary search tree, everything in a node's left subtree is smaller than the node, and everything in its right subtree is larger. An in-order traversal (the left subtree, then the node, then the right subtree) therefore visits the values in increasing order. The k-th node it visits holds the answer.

    The simplest way to use that is to collect every value into a list and take the element at index k - 1. It works, but it visits the whole tree even when k is small. The follow-up asks to stop as soon as the answer is known, in O(h + k) time, where h is the height.

    A recursive traversal can't just return in the middle of its calls, so the lesson writes the traversal with an explicit stack, which can stop at any moment. The stack holds the nodes passed on the way down that still wait to be visited:

    1. Go as far left as possible, pushing every node on the way. The last node pushed is the smallest one not yet visited.
    2. Pop a node. This is the next value in increasing order. Count it.
    3. If the count is k, return its value.
    4. Otherwise move to its right child and repeat. The right subtree holds values larger than the popped node but smaller than the nodes still on the stack.

    On the tree with root 5 and k = 3: the walk pushes 5, 3, 2 and 1. It pops 1 (count 1) and finds no right child, then pops 2 (count 2), then pops 3 (count 3) and returns 3. The nodes 4, 5 and 6 are never visited.

    Step by step

    1. Create the stack. stack is a new, empty Stack<TreeNode>. Push puts a node on top and Pop takes the top one off.
    2. Create node, set to root. It is the part of the tree still to walk through.
    3. Create count at 0: how many nodes have been visited.
    4. Outer loop. Run while node != null or the stack is not empty (stack.Count > 0). The traversal is over only when there is nothing to walk into and nothing waiting.
    5. Inner loop. While node != null, head to the leftmost node, since smaller values are on the left.
    6. Push node inside the inner loop. It waits there until its left subtree has been visited.
    7. Move left with node = node.left.
    8. Pop the top node into node once the inner loop ends. It is the smallest value not yet visited.
    9. Visit it with count++.
    10. Check count == k. If so, return node.val.
    11. Move right with node = node.right. The next pass walks through that subtree.
    12. Return -1 after the loop. Since k is at most the number of nodes, the loop returns first. C# requires a return on every path.

    The solution in C#

    class Solution {
        public int KthSmallest(TreeNode root, int k) {
            Stack<TreeNode> stack = new Stack<TreeNode>();
            TreeNode node = root;
            int count = 0;
            while (node != null || stack.Count > 0) {
                while (node != null) {
                    stack.Push(node);
                    node = node.left;
                }
                node = stack.Pop();
                count++;
                if (count == k) {
                    return node.val;
                }
                node = node.right;
            }
            return -1;
        }
    }
    

    The C# details worth knowing:

    • Stack<T> lives in System.Collections.Generic. A standalone file needs using System.Collections.Generic;. Its size is the property Count, not Length.
    • Pop throws on an empty stack. It raises an InvalidOperationException. The loop condition guarantees that, after the inner loop, the stack holds a node: either it was non-empty to begin with, or node was non-null and has just been pushed. So Pop is safe here.
    • node does double duty. It is the cursor while walking left, and it holds the popped node afterwards. Assigning to it never changes the tree, only where the variable points.
    • count++ as a statement adds 1 to count. Its old value isn't used here, so ++count would do the same.
    • Why the final return -1; is there. The compiler can't prove that the loop always returns, and a method returning int must return on every path.

    Complexity

    Let h be the height of the tree and n the number of nodes.

    Time: O(h + k). The first descent pushes at most h nodes. After that, every pop counts one more node in sorted order, and the method stops at the k-th pop. Every node is pushed once before it is popped, so the number of pushes is at most the number of pops plus the nodes still on the stack: at most k + h. In the worst case, k = n, this becomes O(n).

    Space: O(h). The stack holds the path from the root to the current node: about log n nodes for a balanced tree and up to n for a chain. Because the traversal is iterative, a deep tree uses heap memory for the stack and doesn't risk exhausting the call stack, as deep recursion could.

    Edge cases and common mistakes

    • One node. With a single node and k = 1, the walk pushes it, pops it, counts 1 and returns its value.
    • The smallest and the largest. In the second example tree, k = 1 returns 1 and k = 6 returns 6.
    • A chain going left. If every node has only a left child, the first descent pushes them all. k = 1 returns after that single descent, and k = n returns the root, which is the largest value.
    • A right child in the middle. In the first example tree, the node 1 has only a right child. After popping 1, the walk moves right to 2, which is the correct next value. Skipping the move to node.right would lose it.
    • Counting from 0. The statement counts from 1. Starting count at 1, or comparing with k - 1 without adjusting, returns the wrong node.
    • Forgetting the second part of the loop condition. With only node != null, the loop would end as soon as node becomes null after a leaf, while nodes are still waiting on the stack.

    Testing

    • Tree [3, 1, 4, null, 2] with k = 1 gives 1, with k = 2 gives 2 and with k = 4 gives 4.
    • Tree [5, 3, 6, 2, 4, null, null, 1] with k = 3 gives 3.
    • The same tree with k = 6 gives 6.
    • A one-node tree with k = 1 gives that node's value.
    Keep going with C#About 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free