Start learning free

LESSON 342 OF 438 · BINARY TREES

Kth Smallest Element in a BST in JavaScript: 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

    JAVASCRIPT · BINARY TREES

    Kth Smallest Element In Bst in JavaScript, explained

    Problem overview

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

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

    The follow-up asks you to stop as soon as the answer is known, in O(h + k) time, where h is the height of the tree.

    Approach: in-order traversal, stopped early

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

    A recursive in-order traversal would collect every value, or need extra work to stop in the middle. This lesson uses an explicit stack instead, which makes stopping natural: the loop simply returns when the count reaches k, and the rest of the tree is never touched.

    The stack holds the nodes on the way down that are waiting for their turn. From any node, the smallest value in its part of the tree is at its leftmost node. So the walk goes left as far as possible, pushing each node it passes. Then it pops the top node, which is the smallest one not yet visited, counts it, and continues with that node's right subtree, whose values come next in order.

    Take the tree with root 3, left child 1 (right child 2) and right child 4, and k = 3. Going left from 3 pushes 3, then 1. Pop 1: count is 1. Its right child is 2, so push 2 and pop it: count is 2. It has no right child, so the next pop gives 3: count is 3, which equals k, and the method returns 3.

    Step by step

    1. Create stack as an empty array, with const, since the variable is never reassigned. push and pop both work at the end of an array, so an array serves as a stack.
    2. Declare node with let and set it to root. It is the root of the part of the tree still to be walked through. When it is null, the next node to visit is the one on top of the stack.
    3. Declare count with let, starting at 0. It counts the nodes visited so far.
    4. Write the outer while loop with the condition node || stack.length > 0. The traversal is finished only when nothing remains to walk through and no node is waiting on the stack.
    5. Write an inner while (node) loop to head for the leftmost node, because each step to the left leads to smaller values.
    6. Push node onto the stack. Its left subtree holds smaller values, so it must wait until they have been visited.
    7. Move node to node.left. This ends the inner loop when the walk falls off the left edge.
    8. After the inner loop, pop the top of the stack into node. This is the smallest value not yet visited: everything smaller, including its whole left subtree, has been visited already.
    9. Visit it with count++.
    10. If count === k, return node.val. This is the early exit that gives O(h + k).
    11. Otherwise set node to node.right. The next values in order are in the right subtree. They are larger than this node and smaller than whatever is still on the stack, so the next pass walks through them first.
    12. After the outer loop, return -1. Since k is at most the number of nodes, the method returns earlier. The line makes sure a number is returned on every path.

    The solution in JavaScript

    class Solution {
        kthSmallest(root, k) {
            const stack = [];
            let node = root;
            let count = 0;
            while (node || stack.length > 0) {
                while (node) {
                    stack.push(node);
                    node = node.left;
                }
                node = stack.pop();
                count++;
                if (count === k) {
                    return node.val;
                }
                node = node.right;
            }
            return -1;
        }
    }

    Some details about JavaScript:

    • const doesn't make the array immutable. const stack = [] only stops the name stack from being rebound. Pushing and popping still change the array.
    • pop() returns undefined on an empty array. Here it never does: the outer condition is true on a pass only if node is a node or the stack is not empty, and the inner loop pushes at least one node whenever node is a node. So the stack is never empty at the pop.
    • node || stack.length > 0 relies on truthiness. A node is an object, so it is truthy, and null is falsy. A node whose val is 0 is still truthy.
    • === compares count and k without conversion. Both are numbers, so it behaves like the comparison you expect.

    Complexity

    Going down to the first leftmost node pushes at most h nodes. After that, each node visited is popped once, and each push is paid for by a later pop, so reaching the k-th node takes O(h + k) steps. Even without the early return, every node is pushed and popped at most once, so the whole traversal is O(n).

    The stack holds one path from the root, so the extra space is O(h): about log n for a balanced tree and n for a chain.

    Edge cases and common mistakes

    • Counting from 0. k starts at 1. Checking the count before incrementing it would return the wrong node.
    • Forgetting the right subtree. Without node = node.right after the visit, the loop would pop the next stack node and skip every right child. On the tree with root 3, left child 1 and 1's right child 2, the second smallest value, 2, would be missed.
    • Stopping the outer loop when node is null. After a pop, node may be null while the stack still holds nodes. That is why the condition also checks the stack.
    • A single-node tree. With k = 1, the walk pushes the root, pops it, counts 1 and returns its value.
    • A chain tilted to one side. The stack can grow to n nodes when the tree only has left children. Right-only chains keep the stack at one node.

    Testing

    On the tree with root 5, children 3 and 6, 2 and 4 under 3, and 1 under 2, kthSmallest(root, k) returns 1, 2, 3, 4, 5 and 6 for k from 1 to 6.

    Keep going with JavaScriptAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free