Right Smaller Than
Right Smaller Than
Category
Binary Search Trees
Difficulty
Hard
Problem Statement
Given an array of integers, return a new array where each element represents the number of integers to the right of that element that are strictly smaller than it. For example, given [8, 5, 11, -1, 3, 4, 2], the output would be [5, 4, 4, 0, 1, 1, 0].
Intuition
A brute-force approach checking all pairs is O(n^2). We can do better by processing the array from right to left and inserting each element into a self-balancing BST. At each insertion, we can count how many previously inserted elements (elements to the right in the original array) are smaller. By augmenting each BST node with the size of its left subtree, we can compute this count during insertion in O(h) time per element.
Approach
- Initialize an empty result array of the same length as the input.
- Iterate through the input array from right to left.
- For each element, insert it into an augmented BST where each node tracks
leftSubtreeSize(count of nodes in its left subtree). - During insertion, whenever we go right (the new value is greater than or equal to the current node), add
leftSubtreeSize + 1to a running count (these are all the nodes smaller than the new value at this point). - When we go left, increment the current node's
leftSubtreeSize. - Store the running count as the result for this position.
- Return the result array.
Pseudocode
function rightSmallerThan(array):
if array is empty:
return []
result = array of same length
bstRoot = null
for i from length - 1 down to 0:
bstRoot = insertAndCount(bstRoot, array[i], 0, result, i)
return result
function insertAndCount(node, value, numSmaller, result, index):
if node is null:
result[index] = numSmaller
return new BSTNode(value)
if value < node.value:
node.leftSubtreeSize += 1
node.left = insertAndCount(node.left, value, numSmaller, result, index)
else:
numSmaller += node.leftSubtreeSize
if value > node.value:
numSmaller += 1
node.right = insertAndCount(node.right, value, numSmaller, result, index)
return node
class BSTNode:
value
leftSubtreeSize = 0
left = null
right = null
Time & Space Complexity
- Time: O(n log n) on average, where n is the length of the array. Each insertion into the BST takes O(h) time. For a balanced BST, h = O(log n). In the worst case (sorted input), the BST degenerates to O(n) height, making the total O(n^2). Using a self-balancing BST (AVL, Red-Black) guarantees O(n log n).
- Space: O(n) for the BST and the result array.
Key Insights
- Processing right to left ensures that when we insert an element, all elements to its right have already been inserted.
- The
leftSubtreeSizeaugmentation is the key optimization — it allows counting smaller elements without traversing the entire left subtree. - When going right during insertion, we add
leftSubtreeSize + 1because all nodes in the left subtree plus the current node are smaller. - When the value equals the current node's value, we do NOT add 1 for the current node (strictly smaller), but we DO add the
leftSubtreeSize. - Alternative approaches include merge sort (counting inversions), Binary Indexed Trees (BIT/Fenwick trees), or segment trees.
Edge Cases
- Empty array — return an empty array.
- Array with one element — return
[0]. - Array sorted in ascending order — the BST degenerates to a linked list; every result is 0.
- Array sorted in descending order — the BST also degenerates; results are
[n-1, n-2, ..., 1, 0]. - Array with duplicate values — handled correctly by the strict less-than comparison during insertion.
- All elements are the same — every result is 0.