Skip to content
dsa450for five year olds

Binary Search Trees · 13 of 22

Find Kth smallest element in a BST

Count your way up through a sorted tree from the smallest number and stop at the k-th one, without writing the whole tree down.

Simple way
time O(n)
room O(n)
Clever way
time O(h + k)
room O(h)

The problem, simply

You get a binary search tree (BST) and a number k, counting from 1. Give back the k-th smallest number in the tree. For k = 1 that is the smallest, for k = 2 the second smallest, and so on.

A story to remember it

Picture it

tree
841226101413574th2nd1st3rd5th
arraysorted order
01234567891012345678101214k=5

Read the tree left side, me, right side and you get 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14. The 5th one is 5.

  • answer

Examples

In the middle

In
tree above, k = 5
Out
5

Count 1, 2, 3, 4, 5. Stop.

The smallest

In
tree above, k = 1
Out
1

Slide all the way left. The first spot you count is the answer.

The biggest

In
tree above, k = 11
Out
14

k equals the number of spots, so the answer is the biggest. We have to count every spot.

A stick going left

In
[3, 2, null, 1], k = 2
Out
2

The pile holds 3, 2, 1. Pop 1 (first), pop 2 (second). Done.

Way 1: the simple way

Write the whole sorted order down and pick position k - 1.

  1. Visit left side, me, right side for every spot and add each number to a list.
  2. Return list.get(k - 1).
java
import java.util.*;

class Solution {
    public int kthSmallest(TreeNode root, int k) {
        List<Integer> sorted = new ArrayList<>();
        inorder(root, sorted);
        return sorted.get(k - 1);
    }

    private void inorder(TreeNode node, List<Integer> sorted) {
        if (node == null) return;
        inorder(node.left, sorted);
        sorted.add(node.val);
        inorder(node.right, sorted);
    }
}

How long it takes: every spot, O(n), even when k is 1. Extra room: the list, O(n).

Way 2: the clever way

Walk in sorted order with your own pile (a stack) and stop at the k-th spot.

  1. Push the left road from the top: the top, its left child, that one's left child, and so on.
  2. Pop the top of the pile. That is the next smallest number. Count it.
  3. If the count is k, return it.
  4. Otherwise push the left road starting from the popped spot's right child, and go back to step 2.

Count up to k = 5 with a pile

01 / 06

tree
84122610141357
stackpile
8421top

Push the left road: 8, 4, 2, 1. The top of the pile, 1, is the smallest number in the tree.

count
0
k
5
  • looking now
  • done
  • answer
  • path
  • just added
  • ignored
java
import java.util.*;

class Solution {
    public int kthSmallest(TreeNode root, int k) {
        Deque<TreeNode> pile = new ArrayDeque<>();
        TreeNode curr = root;
        while (curr != null || !pile.isEmpty()) {
            while (curr != null) {          // push the left road
                pile.push(curr);
                curr = curr.left;
            }
            curr = pile.pop();              // the next smallest
            if (--k == 0) return curr.val;  // that was the k-th
            curr = curr.right;              // its right side comes next
        }
        return -1;                          // k is bigger than the tree
    }
}

How long it takes: walking down the first left road is h steps, and then we pop k times. So O(h + k). Extra room: the pile is at most one road tall, O(h).

Another way: count sizes for many questions

If the tree changes often and you are asked for the k-th smallest many times, store in every spot how many spots its left side has (leftSize). Then one walk answers a question:

  • If k == leftSize + 1, this spot is the answer.
  • If k <= leftSize, go left.
  • Otherwise go right with k - leftSize - 1.
java
class SizedNode {
    int val, leftSize;                       // how many spots are on my left side
    SizedNode left, right;
    SizedNode(int v) { val = v; }
}

class Solution {
    // insert keeps leftSize correct: going left means one more on that side
    SizedNode insert(SizedNode node, int v) {
        if (node == null) return new SizedNode(v);
        if (v < node.val) { node.leftSize++; node.left = insert(node.left, v); }
        else node.right = insert(node.right, v);
        return node;
    }

    int kthSmallest(SizedNode node, int k) {
        while (node != null) {
            if (k == node.leftSize + 1) return node.val;
            if (k <= node.leftSize) node = node.left;
            else { k -= node.leftSize + 1; node = node.right; }
        }
        return -1;
    }
}

Each question and each insert is O(h). In a balanced tree that is O(log n), even with millions of questions.

Watch out for

  • Recursive versions need a shared counter (a field or an array). A plain int parameter is copied, so counts from the left side are lost.
  • Recursive versions should stop early once the answer is found, or they quietly walk the whole tree.
  • k larger than the tree size. Say what you return (here -1), or throw.
  • A Morris walk gives O(1) room, but if you return early in the middle, threads are left in the tree. Finish the walk or undo them first.

What interviewers ask next

  • The tree changes often and k-th smallest is asked often. Use the leftSize idea above. This is called an order statistic tree.
  • k-th largest instead. Walk right side, me, left side. See "Find Kth largest element in a BST".
  • Median of the BST. Count the spots, then find the middle one or two with this walk. See "Find the median of BST in O(n) time and O(1) space".
  • k smallest numbers in a plain unsorted array. Use a max heap of size k, O(n log k), or quickselect, O(n) on average.

Remember this

Grown-up words: reading left, me, right is an inorder traversal, the pile is the stack of an iterative inorder walk, O(h + k) is "the height to get started plus k pops", and the spot that knows its left side's size is an augmented node in an order statistic tree.