Start learning free

LESSON 341 OF 437 · 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 function kthSmallest receives the root of a binary search tree and an integer k counted from 1, and returns the k-th smallest value in the tree. The values are distinct and 1 <= k <= n, where n is the number of nodes.

    For the tree with root 5, children 3 and 6, where 3 has children 2 and 4 and 2 has a left child 1, the sorted values are 1, 2, 3, 4, 5, 6. So k = 3 gives 3 and k = 6 gives 6. For the tree [3,1,4,NULL,2] with k = 1 the answer is 1.

    Approach: in-order traversal that stops early

    An in-order traversal visits the left subtree, then the node, then the right subtree. In a BST that visits the values in increasing order, so the k-th node visited holds the answer.

    A recursive traversal is the usual way to write it, but it makes stopping at the k-th node awkward. The lesson uses an explicit stack instead, which turns the traversal into a loop that can simply return. The stack holds the nodes on the way down whose left subtree is still being visited. This also gives the follow-up's bound: reaching the first node costs about h pushes, where h is the height, and each further visit costs a pop and a few pushes, so the work is O(h + k) rather than O(n).

    On the second tree with k = 3: the walk pushes 5, 3, 2, 1 going left. It pops 1 (count 1), has no right child, pops 2 (count 2), pops 3 (count 3) and returns 3. Nodes 4, 5 and 6 are never visited.

    Step by step

    1. Create an empty stack of node pointers. It holds the nodes passed on the way down that wait to be visited.
    2. Set node to the root. It is the root of the part of the tree that is still to be walked.
    3. Count visits with count, starting at 0.
    4. Loop while there is work left: node is not null, or the stack is not empty. The traversal is finished only when neither holds.
    5. Walk to the leftmost node with an inner loop that runs while node is not null, because the smallest value of any subtree is at its leftmost node.
    6. Push each node on the way down. Its left side holds smaller values, so it has to wait.
    7. Move to the left child.
    8. Take the next node to visit. After the inner loop, node is null. Read the top of the stack with top(), then remove it with pop(). These are two calls, because pop() returns nothing.
    9. Count the visit with count++.
    10. Check for the answer. If count == k, return this node's value.
    11. Otherwise go right. The next larger values are in this node's right subtree, so set node = node->right and let the next pass of the outer loop handle it.
    12. Return -1 after the loop. Since k is at most n, the function always returns earlier. The line just gives the function a return value on every path.

    The solution in C++

    class Solution {
    public:
        int kthSmallest(TreeNode* root, int k) {
            stack<TreeNode*> stack;
            TreeNode* node = root;
            int count = 0;
            while (node != NULL || !stack.empty()) {
                while (node != NULL) {
                    stack.push(node);
                    node = node->left;
                }
                node = stack.top();
                stack.pop();
                count++;
                if (count == k) {
                    return node->val;
                }
                node = node->right;
            }
            return -1;
        }
    };
    

    The C++ details worth a closer look:

    • std::stack. push adds on top, top reads the top element, pop removes it without returning it, and empty tests for no elements. By default it is built on a deque.
    • top() before pop(). Once an element is popped, a reference to it is invalid. The code copies the pointer into node first, then pops.
    • Naming the stack stack. The variable has the same name as the type. That is legal here, because the declaration stack<TreeNode*> stack; is read with the type first, but inside the function the name now refers to the variable. Another name, such as st, avoids confusion.
    • Calling top() on an empty stack is undefined behaviour. Here it can't happen: the outer loop only runs while node is non-null or the stack is not empty, and a non-null node is pushed by the inner loop first.
    • Pointers on the stack. The stack stores copies of the pointers, not the nodes, so pushing is cheap and the tree is not modified.

    Complexity

    Time: O(h + k). The first descent pushes at most h nodes. After that, every visit pops one node, and the pushes of the later descents add up to at most the nodes that are visited or still waiting, so the whole run does O(h + k) pushes and pops. In the worst case, k = n, this is O(n).

    Space: O(h). The stack holds at most one path from the root down, which is h nodes: about log n for a balanced tree and n for a chain.

    Edge cases and common mistakes

    • k = 1. The answer is the leftmost node. The first pop returns it.
    • k = n. The answer is the largest value, and the whole tree is visited.
    • A single node. k can only be 1, and the root is returned.
    • No left children. For a chain that goes right, the stack never holds more than one node, and the values come out in order anyway.
    • Forgetting node = node->right. Without it, the same node would be pushed again and the loop would never end, or it would skip right subtrees.
    • Counting before the walk to the left. The count must go up when a node is visited (popped), not when it is pushed. Pushing happens before the smaller values have been seen.
    • Treating k as 0-based. The first smallest value is k = 1.

    Testing

    • [3,1,4,NULL,2] with k = 1 gives 1.
    • [5,3,6,2,4,NULL,NULL,1] with k = 3 gives 3.
    • [5,3,6,2,4,NULL,NULL,1] with k = 6 gives 6.
    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