Problem
Given an integer array nums
, return an integer array counts
where counts[i]
is the number of smaller elements to the right of nums[i]
.
Examples
Example 1:
|
|
Example 2:
|
|
Example 3:
|
|
Solution
Method 1 - Merge sort
Here is the approach:
- Modified Merge Sort:
- We use merge sort to both sort the array and count the smaller elements to the right.
- During the merge step, we count how many elements from the right part are moved before an element from the left part. This count is the number of smaller elements to the right for that element.
Then, we can following steps:
- Initialization: Create a list of tuples where each tuple contains an element and its original index.
- Merge Sort: Sort the array while maintaining the original indices and counting the smaller elements.
- Count Update: During the merge step, update the counts for each element.
Code
|
|
|
|
Complexity
- ⏰ Time complexity:
O(n log n)
due to the merge sort algorithm. - 🧺 Space complexity:
O(n)
due to the temporary arrays used during the merge process.
Method 2 - Binary search tree
We can use BST or balanced BST like AVL tree. Here is the approach:
- TreeNode Class: Define a TreeNode class that represents each node in the BST along with a count of how many times this value has been inserted.
- Insert and Count: Traverse the array from right to left, and for each element, insert it into the BST while counting how many elements are smaller than it.
- Count Smaller: Use a helper function to insert elements into the BST and count the smaller elements to the right.
Code
|
|
|
|
Complexity
- ⏰ Time complexity:
O(n log n)
- 🧺 Space complexity:
O(n)
, due to the storage in the BST and the result list.
Method 3 - Using AVL Tree
We can solve the problem more efficiently using a balanced BST such as an AVL tree. Observe that if a key is greater than the root, it is greater than all the nodes in the left subtree of the root.
We will treat each number in the array as a node in a BST and insert them into an AVL tree one by one, from right to left. During each insertion, we will update the size of the left subtree at the node being inserted. This allows us to keep track of the number of smaller elements. Additionally, we need to handle balancing the tree during insertion.
An AVL tree becomes imbalanced during an insertion when the height difference between the left and right subtrees of a node exceeds 1. There are four cases to consider for rebalancing the tree:
- Insert in the Left Subtree of the Left Child:
- Perform a right rotation on node
x
to balance it.
- Perform a right rotation on node
- Insert in the Right Subtree of the Right Child:
- Perform a left rotation on node
x
to balance it.
- Perform a left rotation on node
- Insert in the Right Subtree of the Left Child:
- Perform a left rotation on the left child of
x
, followed by a right rotation onx
to balance the tree.
- Perform a left rotation on the left child of
- Insert in the Left Subtree of the Right Child:
- Perform a right rotation on the right child of
x
, followed by a left rotation onx
to balance the tree.
- Perform a right rotation on the right child of
By maintaining the balance of the AVL tree during insertions and updating the size of the left subtree, we can efficiently count the number of smaller elements to the right of each element in the array.
Here is the approach:
- Define AVL Tree Node: Each node will keep track of its value, height, size of the left subtree, and a count of elements.
- Insert and Rotate Functions: Implement the necessary AVL tree operations (insert, rotate left, rotate right, update height, and size).
- Main Logic: Traverse the array from right to left, inserting elements into the AVL tree and maintaining a count of elements smaller than each inserted element.
Code
|
|
|
|
Method 4 - Binary search
Here is the approach:
- Use a list to keep track of the sorted elements that have been processed.
- For each element in the array (iterating from right to left), use binary search to find the index where the current element should be inserted in the sorted list.
- The index found using binary search gives the count of smaller elements to the right of the current element.
- Insert the current element into the sorted list at the correct position.
Code
|
|
|
|
Complexity
- ⏰ Time complexity:
O(n log n)
- 🧺 Space complexity:
O(n)
, to store thesorted_list
.
Method 5 - Segment tree
Here is the approach:
- Handling Negative Numbers: Normalize the array by offsetting negative values.
- Segment Tree Operations: Implement the segment tree with operations to add elements, delete elements, and query counts within a range.
Code
|
|
|
|
Complexity
- ⏰ Time complexity:
O(n log n)
- 🧺 Space complexity:
O(n)
Method 6 - Binary Indexed tree
Approach
- Normalize the Values: Compress the values of
nums
to a smaller range to efficiently use the BIT. - Use BIT: Traverse the array from right to left, and for each element, use the BIT to count the number of elements seen so far that are smaller.
- Update BIT: After counting for the current element, update the BIT.
Detailed Steps
- Coordinate Compression: Use sorted unique values to map the original values to their ranks.
- BIT Operations: Implement
update
andquery
functions for BIT to support increment and prefix sum operations.
Code
|
|
|
|
Complexity
- ⏰ Time complexity:
O(n log n)
, due to BIT operations and coordinate compression. - 🧺 Space complexity:
O(n)
, for the BIT and additional data structures.