LeetCode #230 Medium

Kth Smallest Element in BST

Kth Smallest Element in a BST is LeetCode 230 (Medium). You get the root of a binary search tree and an integer k. Return the k-th smallest value among all its nodes, counting from 1.

  • In a binary search tree (BST), every value in a node's left subtree is smaller than the node, and every value in its right subtree is larger.
  • The tree has n nodes, with 1 ≤ k ≤ n ≤ 10⁴ and values from 0 to 10⁴.
  • Follow-up: what if the tree changes often and the k-th smallest is asked for many times?
Constraints
  • The number of nodes in the tree is n.
  • 1 <= k <= n <= 10⁴
  • 0 <= Node.val <= 10⁴
bstinorderdfs
Open on LeetCode ↗
02

Intuition

An inorder traversal visits a node's left subtree, then the node, then its right subtree. In a BST that means smaller values, then the node, then larger values, so inorder reads the values in ascending order.

The kth smallest element in BST is therefore the k-th node that inorder visits. Count the visits and stop at the k-th; the rest of the tree is never touched.

An explicit stack makes stopping easy. Nodes waiting for their turn sit on the stack, and each pop hands back the next smallest value.

How to spot this pattern

A question about order in a BST, such as the k-th smallest value, the next larger value, or whether the tree is valid, is an inorder traversal with a check in place of printing. Besides this kth smallest element in a BST LeetCode problem, Validate Binary Search Tree (98), Binary Search Tree Iterator (173) and Minimum Absolute Difference in BST (530) use the same stack loop.

03

Approach

Try it first

Before reading on, list the values of [5,3,6,2,4,null,null,1] in ascending order without sorting, just by walking the tree. Which nodes did you pass before writing down the first value?

1

Walk left, stacking the path

From the current node, push it and move to its left child, until there is no left child. Every pushed node is still waiting, because its smaller values on the left come first. The top of the stack is now the smallest value not yet visited.

2

Pop the next smallest and count it

Pop the top node; it is the next value in ascending order. Decrease k by one, and if k reaches 0, return this node's value. In the kth smallest element in a BST Python code, the stack is a plain list, so append pushes and pop takes the top.

3

Move to the right subtree

Everything smaller than the popped node is done, so the next larger values are in its right subtree. Set node = node.right and go back to walking left. If there is no right child, the next pop returns the nearest ancestor still waiting.

4

Follow-up: a tree that changes often

Store in each node the size of its left subtree, L. To find the k-th smallest, start at the root:

  • k <= L: go left.
  • k == L + 1: this node is the answer.
  • k > L + 1: go right with k − L − 1.

Each query is O(h); an insert or delete updates the sizes along its path.

04

Kth Smallest Element in BST solution in Python | C++ | Java

▶1class Solution:
▶2 def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:
▶3 stack = []
▶4 node = root
▶5 while True:
▶6 while node:
▶7 stack.append(node)
▶8 node = node.left
▶9 node = stack.pop()
▶10 k -= 1
▶11 if k == 0:
▶12 return node.val
▶13 node = node.right
on stackvisited3142stackemptyvisitedemptynode = root (3), k = 1
node3start at the root
k1visits still to make
Start at the root. Inorder visits the values smallest first, so the answer is the 1st node it visits. The smallest value is down the left side, so the walk heads left first, and the stack remembers every node it passes.
on stackvisited3142stack3topvisitedemptypush 3, go left to 1
push3smaller values come first
stack[3]top on the right
3 has a left child, and every value down there is smaller, so they must be counted first. Push 3 to come back to it, and move left to 1.
on stackvisited3142stack31topvisitedemptypush 1, no left child
push1smaller values come first
stack[3, 1]top on the right
Push 1. It has no left child, so nothing in the tree that is still unvisited is smaller: 1 is next in order and is now on top of the stack.
on stackvisited3142stack3topvisited11pop 1: visit #1, k = 0 → return 1
pop1visit number 1
k0
return1
1 is the 1st value visited, and inorder visits values in ascending order, so it is the 1st smallest. Return it at once; the 3 nodes never visited are not needed.
05

Recursive inorder with a counter

inorder searches the left subtree first and passes back its answer if it found one. Otherwise it counts the current node, returns its value when the count reaches k, and searches the right subtree.

▶1class Solution:
▶2 def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:
▶3 def inorder(node: Optional[TreeNode]) -> Optional[int]:
▶4 nonlocal k
▶5 if not node:
▶6 return None
▶7 found = inorder(node.left)
▶8 if found is not None:
▶9 return found
▶10 k -= 1
▶11 if k == 0:
▶12 return node.val
▶13 return inorder(node.right)
▶14 
▶15 return inorder(root)
06

Common pitfalls

Collecting every value before answering

✗ Wrong
vals = []
def dfs(n):
    if n:
        dfs(n.left)
        vals.append(n.val)
        dfs(n.right)
dfs(root)
return vals[k - 1]
✓ Right
node = stack.pop()
k -= 1
if k == 0:
    return node.val

It gives the right answer, but it visits all n nodes and stores n values when the answer is known after k visits. Stopping at the k-th pop costs O(h + k).

Counting a node when it is pushed

✗ Wrong
while node:
    stack.append(node)
    k -= 1
    node = node.left
✓ Right
node = stack.pop()
k -= 1
if k == 0:
    return node.val

Nodes are pushed on the way down the left side, largest first, long before their turn. A node takes its place in sorted order only when it comes off the stack.

Going left after a pop

✗ Wrong
node = stack.pop()
...
node = node.left
✓ Right
node = stack.pop()
...
node = node.right

The popped node's left subtree is already finished; that is why it was popped. Going left again repeats those nodes forever. The unvisited, larger values are on the right.

07

Complexity

Time
O(h + k)
Space
O(h)
h is the tree height. The first walk down costs O(h), and each of the k pops adds a little more. The stack holds one path: O(log n) when the tree is balanced, O(n) for a long chain.
08

Kth Smallest Element in BST FAQ

How do you find the kth largest element in a BST?

Mirror the walk: go right first, then the node, then left. That visits the values in descending order, so the k-th pop is the k-th largest.

Can the kth smallest element in a BST be found with O(1) extra space?

Yes, with Morris traversal. Before going left, it links the rightmost node of the left subtree back to the current node, so the walk can climb back up without a stack, and it removes each link on the way back. Count the visits exactly as above and stop at the k-th. Time is O(n), extra space O(1), and the tree is restored.