TL;DR
Inorder traversal, stop after k nodes β O(h + k) time, O(h) space (h = tree height).
Approach 1 β Brute force: collect everything, then sort/index
The naive move ignores the BST property entirely: gather every value with any
traversal, sort the list, and take index k - 1.
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:
vals: List[int] = []
def collect(node: Optional[TreeNode]) -> None:
if not node:
return
vals.append(node.val)
collect(node.left)
collect(node.right)
collect(root)
vals.sort()
return vals[k - 1]
Complexity: O(n log n) time, O(n) space. With n <= 10^4 this actually passes,
but the sort is pure waste β the tree already is sorted data, so throwing that
structure away and re-sorting is the tell that weβve missed the point.
Approach 2 β Recursive inorder with an early-stopping counter
The insight: an inorder traversal (left, node, right) of a BST visits values
in strictly increasing order. So the k-th node visited inorder is the
answer β no list, no sort. Keep a countdown and abort the recursion the moment
it reaches zero.
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:
self.remaining = k
self.answer = -1
def inorder(node: Optional[TreeNode]) -> None:
if not node or self.remaining == 0:
return
inorder(node.left)
if self.remaining == 0:
return
self.remaining -= 1
if self.remaining == 0:
self.answer = node.val
return
inorder(node.right)
inorder(root)
return self.answer
Walkthrough on root = [5,3,6,2,4,null,null,1], k = 3: inorder dives
left to 1 (remaining 3β2), backs up to 2 (2β1), backs up to 3 (1β0) β
answer recorded as 3; every check of remaining == 0 from here on returns
immediately, so 4, 5, 6 are never processed.
Complexity: O(h + k) time (walk down the left spine, then visit k nodes),
O(h) recursion stack.
Approach 3 β Iterative inorder with an explicit stack (the classic)
The insight: the same traversal can be driven by an explicit stack β push
the left spine, pop, step right β which makes the early exit a plain return
instead of threaded flags, and avoids recursion-depth worries on skewed trees.
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:
stack: List[TreeNode] = []
node = root
while node or stack:
while node:
stack.append(node)
node = node.left
node = stack.pop()
k -= 1
if k == 0:
return node.val
node = node.right
return -1 # unreachable given 1 <= k <= n
Walkthrough on root = [3,1,4,null,2], k = 1: push 3, push 1 (no
left child) β pop 1, k becomes 0 β return 1. Two pushes, one pop β the
nodes 2, 3, 4 are never touched.
Complexity: O(h + k) time, O(h) stack space.
Follow-up worth knowing: if the tree is modified often and kthSmallest is
called repeatedly, augment each node with the size of its left subtree; then
each query walks one root-to-node path in O(h) by comparing k against subtree
sizes (an order-statistics tree).
Common pitfalls
- Decrementing
k at the wrong spot (before recursing left, or after moving
right) β the count must happen exactly when a node is visited inorder.
- Forgetting the early exit in the recursive version, so the traversal keeps
mutating state after the answer is found and can overwrite it.
- Off-by-one:
k is 1-indexed, so the brute force needs vals[k - 1].
- Assuming O(log n) height β a skewed BST has h = n, so βO(k)β claims that
ignore the left-spine descent are wrong.
Pattern takeaway
Inorder traversal of a BST is a sorted stream. Any βk-th / rank / rangeβ
question on a BST is really a question about consuming that stream lazily β
iterate with an explicit stack, count as you pop, and stop the moment you have
what you need.