LeetCodee

230. Kth Smallest Element in a BST

Jump to Solution: Python Java C++ JavaScript C#

Problem Description

Given the root of a binary search tree, and an integer k, return the kth smallest value (1-indexed) of all the values of the nodes in the tree.

Example 1:

Input: root = [3,1,4,null,2], k = 1
Output: 1

Example 2:

Input: root = [5,3,6,2,4,null,null,1], k = 3
Output: 3

Constraints:

  • The number of nodes in the tree is n.
  • 1 <= k <= n <= 10⁴
  • 0 <= Node.val <= 10⁴

Follow-up:

If the BST is modified often (i.e., we can do insert and delete operations) and you need to find the kth smallest frequently, how would you optimize the solution?

Solution

Python Solution

# 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:
        # Iterative inorder traversal
        stack = []
        curr = root
        
        while curr or stack:
            # Traverse to leftmost node
            while curr:
                stack.append(curr)
                curr = curr.left
            
            # Process current node
            curr = stack.pop()
            k -= 1
            if k == 0:
                return curr.val
            
            # Move to right subtree
            curr = curr.right

Time Complexity: O(H + k)

Where H is the height of the tree. In the worst case, we need to visit H nodes to reach the leftmost leaf, then k nodes to find the kth element.

Space Complexity: O(H)

Where H is the height of the tree, used by the stack.

Java Solution

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public int kthSmallest(TreeNode root, int k) {
        // Iterative inorder traversal
        Stack stack = new Stack<>();
        TreeNode curr = root;
        
        while (curr != null || !stack.isEmpty()) {
            // Traverse to leftmost node
            while (curr != null) {
                stack.push(curr);
                curr = curr.left;
            }
            
            // Process current node
            curr = stack.pop();
            k--;
            if (k == 0) {
                return curr.val;
            }
            
            // Move to right subtree
            curr = curr.right;
        }
        
        return -1; // Should never reach here if k is valid
    }
}

Time Complexity: O(H + k)

Where H is the height of the tree. In the worst case, we need to visit H nodes to reach the leftmost leaf, then k nodes to find the kth element.

Space Complexity: O(H)

Where H is the height of the tree, used by the stack.

C++ Solution

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int kthSmallest(TreeNode* root, int k) {
        // Iterative inorder traversal
        stack stack;
        TreeNode* curr = root;
        
        while (curr || !stack.empty()) {
            // Traverse to leftmost node
            while (curr) {
                stack.push(curr);
                curr = curr->left;
            }
            
            // Process current node
            curr = stack.top();
            stack.pop();
            k--;
            if (k == 0) {
                return curr->val;
            }
            
            // Move to right subtree
            curr = curr->right;
        }
        
        return -1; // Should never reach here if k is valid
    }
};

Time Complexity: O(H + k)

Where H is the height of the tree. In the worst case, we need to visit H nodes to reach the leftmost leaf, then k nodes to find the kth element.

Space Complexity: O(H)

Where H is the height of the tree, used by the stack.

JavaScript Solution

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} k
 * @return {number}
 */
var kthSmallest = function(root, k) {
    // Iterative inorder traversal
    const stack = [];
    let curr = root;
    
    while (curr || stack.length) {
        // Traverse to leftmost node
        while (curr) {
            stack.push(curr);
            curr = curr.left;
        }
        
        // Process current node
        curr = stack.pop();
        k--;
        if (k === 0) {
            return curr.val;
        }
        
        // Move to right subtree
        curr = curr.right;
    }
    
    return -1; // Should never reach here if k is valid
};

Time Complexity: O(H + k)

Where H is the height of the tree. In the worst case, we need to visit H nodes to reach the leftmost leaf, then k nodes to find the kth element.

Space Complexity: O(H)

Where H is the height of the tree, used by the stack.

C# Solution

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     public int val;
 *     public TreeNode left;
 *     public TreeNode right;
 *     public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
public class Solution {
    public int KthSmallest(TreeNode root, int k) {
        // Iterative inorder traversal
        var stack = new Stack();
        TreeNode curr = root;
        
        while (curr != null || stack.Count > 0) {
            // Traverse to leftmost node
            while (curr != null) {
                stack.Push(curr);
                curr = curr.left;
            }
            
            // Process current node
            curr = stack.Pop();
            k--;
            if (k == 0) {
                return curr.val;
            }
            
            // Move to right subtree
            curr = curr.right;
        }
        
        return -1; // Should never reach here if k is valid
    }
}

Time Complexity: O(H + k)

Where H is the height of the tree. In the worst case, we need to visit H nodes to reach the leftmost leaf, then k nodes to find the kth element.

Space Complexity: O(H)

Where H is the height of the tree, used by the stack.

Approach Explanation

The solution uses an iterative inorder traversal of the BST. Here's how it works:

  1. Inorder Traversal Process:
    • Use a stack to keep track of nodes
    • Traverse to the leftmost node first
    • Process nodes in order: left, root, right
    • Stop when we find the kth element
  2. Key Properties:
    • Inorder traversal of BST visits nodes in ascending order
    • No need to traverse the entire tree once we find kth element

For the follow-up question about frequent modifications and queries: We could maintain additional information in each node:

  • Count of nodes in left subtree
  • This allows O(H) time for both updates and queries
  • Trade-off is additional space and maintenance during modifications