The problem can be found at the following link: Question Link
Given a Binary Search Tree (BST) and an integer k, the task is to find the k-th smallest element in the BST.
If there is no k-th smallest element present, return -1
.
2
/ \
1 3
k = 2

2
The elements in ascending order are [1, 2, 3]
. The 2nd smallest element is 2
.
2
/ \
1 3
k = 5

-1
The BST contains only 3 elements. Hence, there is no 5th smallest element.
20
/ \
8 22
/ \
4 12
/ \
10 14
k = 3

10
The in-order traversal of the BST is [4, 8, 10, 12, 14, 20, 22]
.
The 3rd smallest element is 10
.
- 1 ≤ number of nodes, k ≤
$10^5$ - 1 ≤ node->data ≤
$10^5$
- Morris Traversal allows in-order traversal without using a stack or recursion, thus achieving O(1) space complexity.
- It temporarily modifies the tree structure but restores it before completion.
- Start from the root node.
- If the left child is
NULL
:- Visit the current node (decrement
k
). - If
k == 0
, return the current node's value. - Move to the right child.
- Visit the current node (decrement
- If the left child exists:
- Find the rightmost node in the left subtree (inorder predecessor).
- If the rightmost node's right is
NULL
:- Make the current node its right child (create a temporary thread).
- Move to the left child.
- If the rightmost node’s right is the current node (thread exists):
- Remove the thread (restore the original tree).
- Visit the current node (decrement
k
). - If
k == 0
, return the current node's value. - Move to the right child.
- If the traversal completes without finding the k-th smallest, return
-1
.
-
Expected Time Complexity:
O(N)
We visit each node exactly twice (once to create a thread and once to remove it), which is linear with respect to the number of nodes. -
Expected Auxiliary Space Complexity:
O(1)
No additional stack or recursion is used. Only a few pointers are manipulated.
int kthSmallest(struct Node* root, int k) {
while (root) {
if (!root->left) {
if (--k == 0) return root->data;
root = root->right;
} else {
struct Node *pre = root->left;
while (pre->right && pre->right != root) pre = pre->right;
if (!pre->right) {
pre->right = root;
root = root->left;
} else {
pre->right = NULL;
if (--k == 0) return root->data;
root = root->right;
}
}
}
return -1;
}
class Solution {
public:
int kthSmallest(Node* root, int k) {
while (root) {
if (!root->left) {
if (--k == 0) return root->data;
root = root->right;
} else {
Node* pre = root->left;
while (pre->right && pre->right != root) pre = pre->right;
if (!pre->right) {
pre->right = root;
root = root->left;
} else {
pre->right = NULL;
if (--k == 0) return root->data;
root = root->right;
}
}
}
return -1;
}
};
class Solution {
public:
int kthSmallest(Node* root, int &k) {
if (!root) return -1;
int left = kthSmallest(root->left, k);
if (k == 0) return left;
if (--k == 0) return root->data;
return kthSmallest(root->right, k);
}
};
🔹 Optimized by using k
as a reference, reducing redundant calculations.
class Solution {
public:
int kthSmallest(Node* root, int k) {
if (!root) return -1;
stack<Node*> st;
while (true) {
while (root) {
st.push(root);
root = root->left;
}
if (st.empty()) return -1;
root = st.top(); st.pop();
if (--k == 0) return root->data;
root = root->right;
}
}
};
🔹 Handles larger trees efficiently without recursion depth issues.
Approaches | ⏱️ Time Complexity | 🗂️ Space Complexity | ⚡ Method | ✅ Pros | |
---|---|---|---|---|---|
Morris Traversal (Optimized) | 🟢 O(N) |
🟢 O(1) |
No extra space | No extra space used | Modifies tree temporarily |
Recursive Inorder | 🟢 O(N) |
🟡 O(H) |
Recursion | Simple and concise | Stack space for recursion |
Iterative Inorder (Stack) | 🟢 O(H + K) |
🟡 O(H) |
Stack-based | Avoids recursion depth issues | Uses extra memory for stack |
- For space efficiency: ✅ Morris Traversal (
O(1)
space). - For simplicity: ✅ Recursive Inorder Traversal is intuitive.
- For large/deep trees: ✅ Iterative Inorder Traversal (Stack) avoids recursion depth issues.
class Solution {
public int kthSmallest(Node root, int k) {
while (root != null) {
if (root.left == null) {
if (--k == 0) return root.data;
root = root.right;
} else {
Node pre = root.left;
while (pre.right != null && pre.right != root) pre = pre.right;
if (pre.right == null) {
pre.right = root;
root = root.left;
} else {
pre.right = null;
if (--k == 0) return root.data;
root = root.right;
}
}
}
return -1;
}
}
class Solution:
def kthSmallest(self, root, k):
while root:
if not root.left:
k -= 1
if k == 0:
return root.data
root = root.right
else:
pre = root.left
while pre.right and pre.right != root:
pre = pre.right
if not pre.right:
pre.right = root
root = root.left
else:
pre.right = None
k -= 1
if k == 0:
return root.data
root = root.right
return -1
For discussions, questions, or doubts related to this solution, feel free to connect on LinkedIn: Any Questions. Let’s make this learning journey more collaborative!
⭐ If you find this helpful, please give this repository a star! ⭐