hard Reverse Pairs
Problem Statement
Given an array of integers nums, return the count of reverse pairs in the array.
A reverse pairis a pair (i, j) where:
0 <= i < j < nums.lengthandnums[i] > 2 * nums[j].
Examples
-
Example 1:
- Input:
nums = [4, 3, 4, 1] - Expected Output:
3 - Justification: The three reverse pairs are
(0, 3),(1, 3)and(2, 3).
- Input:
-
Example 2:
- Input:
nums = [4, 1, 2] - Expected Output:
1 - Justification: There is only one reverse pair
(0, 1)since4 > 2 * 1.
- Input:
-
Example 3:
- Input:
nums = [6,5,4,3,2,1] - Expected Output:
6 - Justification: The siz reverse pairs are
(0, 4),(0, 5),(1, 4),(1, 5),(2, 5)and(3, 5).
- Input:
Try it yourself
Try solving this question here:
✅ Solution Reverse Pairs
Problem Statement
Given an array of integers nums, return the count of reverse pairs in the array.
A reverse pairis a pair (i, j) where:
0 <= i < j < nums.lengthandnums[i] > 2 * nums[j].
Examples
-
Example 1:
- Input:
nums = [4, 3, 4, 1] - Expected Output:
3 - Justification: The three reverse pairs are
(0, 3),(1, 3)and(2, 3).
- Input:
-
Example 2:
- Input:
nums = [4, 1, 2] - Expected Output:
1 - Justification: There is only one reverse pair
(0, 1)since4 > 2 * 1.
- Input:
-
Example 3:
- Input:
nums = [6,5,4,3,2,1] - Expected Output:
6 - Justification: The siz reverse pairs are
(0, 4),(0, 5),(1, 4),(1, 5),(2, 5)and(3, 5).
- Input:
Solution
To solve this problem, we'll utilize a divide and conquer approach, similar to the merge sort algorithm. This method is effective because it allows us to efficiently compare elements and count reverse pairs during the merge process. As we divide the array into smaller subarrays, we can more manageably identify and count the pairs that meet our criteria before merging them back together. This technique not only helps in sorting the array but also enables us to count the reverse pairs without checking each pair explicitly, significantly reducing the time complexity.
The core idea revolves around dividing the array into two halves, counting the reverse pairs in each half recursively, and then counting the pairs that cross the midpoint during the merge phase. This approach works effectively because it systematically reduces the problem size while ensuring that all potential reverse pairs are counted. The merge step is crucial, as it allows us to count the reverse pairs across the divided parts efficiently, leveraging the sorted order of subarrays to make quicker comparisons.
Step-by-Step Algorithm
-
Implement the recursive function
reversePairsRec:- If the current subarray consists of one element or is invalid (left index greater than right), return 0 since no reverse pairs can be found.
- Calculate the middle index of the current subarray.
- Recursively call
reversePairsRecfor the left half of the array and add the result to thecount. - Recursively call
reversePairsRecfor the right half of the array and add the result to thecount. - Call
mergeAndCountwith the left, middle, and right indices of the current subarray, add this result to the total count, and return it.
-
Count and merge with
mergeAndCount:- Initialize a count of reverse pairs to 0.
- Use two pointers: one starting at the left half's beginning and another at the right half's beginning.
- For each element in the left subarray, iterate through the right subarray to find elements that satisfy the reverse pair condition (left element is more than twice the right element). For each such element found, add to the count the number of remaining elements in the left subarray, as they all will form reverse pairs with the current element in the right subarray.
- Sort the current subarray to ensure the merging process works correctly for future recursive calls.
- Return the count of reverse pairs found in this step.
-
Return Final Count:
- Return the
countof reverse pairs at the end.
- Return the
Algorithm Walkthrough
Let's consider the Input: nums = [6,5,4,3,2,1]
Initial Setup
- We start with the array
[6,5,4,3,2,1]and the goal to count the reverse pairs. - The initial call is
reversePairs([6,5,4,3,2,1]), which translates toreversePairsRec([6,5,4,3,2,1], 0, 5).
First Division
- Divide: The array is split into two halves at the midpoint index
2, resulting in two subarrays:[6,5,4](left) and[3,2,1](right).
Left Half: [6,5,4]
-
Divide Left Half: Further split
[6,5,4]into[6, 5](left) and[4](right).-
[4]is a base case, returns0reverse pairs. -
Left Subarray
[6,5]:- Split into
[6]and[5]. - Both
[6]and[5]are base cases, return0reverse pairs each. - Merge
[6]and[5]:mergeAndCountfinds0reverse pair because6 < 2*5.- After merging, the subarray becomes
[5, 6].
- Split into
-
Right Subarray
[4]:[4]is a base case, returns0reverse pairs. -
Merge
[6, 5]with[4]:- No new reverse pairs found during this merge since
6and5is not more than twice4. - The merged subarray remains
[4,5,6].
- No new reverse pairs found during this merge since
-
Right Half: [3,2,1]
-
Divide Right Half: Split
[3,2,1]into[3, 2](left) and[1](right).-
Left Subarray
[3, 2]:-
Split into
[3]and[2]. -
Both
[3]and[2]are base cases, return0reverse pairs each. -
Merge
[3]and[2]:mergeAndCountfinds0reverse pair because3 < 2*2.- After merging, the subarray becomes
[2, 3].
-
Left Subarray
[1]:[1]is a base case, returns0reverse pairs.
-
-
Merge
[2,3]with[1]:mergeAndCountfinds1reverse pairs:3 > 2*1. Now,reversePairs = 1.- The merged subarray becomes
[1,2,3].
-
Final Merge: [4,5,6] with [1,2,3]
- Merge Process:
- At this stage, both halves
[4,5,6]and[1,2,3]are individually sorted. - During the final merge,
mergeAndCountoperates on these two halves:- For
[4]from the left subarray, it finds1reverse pairs with[1]. Now,reversePairs = reversePairs + 1 = 1 + 1 = 2. - For
[5], it finds2reverse pairs with [1, 2]. Now,reversePairs = reversePairs + 2 = 2 + 2 = 4. - For
[6], it also finds2reverse pairs with [1, 2]. Now,reversePairs = reversePairs + 2 = 4 + 2 = 6.
- For
- At this stage, both halves
- Final State:
- After the merge, the entire array is sorted as
[1,2,3,4,5,6], but most importantly, all reverse pairs have been counted throughout the divide and merge process. The total count of reverse pairs are6.
- After the merge, the entire array is sorted as
Code
import java.util.Arrays;
public class Solution {
// Method to count reverse pairs using a modified merge sort
private int mergeAndCount(int[] nums, int left, int mid, int right) {
int count = 0; // Initialize count to store the number of reverse pairs
int j = mid + 1; // Initialize j to start from the right half of the array
// Count reverse pairs
for (int i = left; i <= mid; i++) { // Iterate through the left half of the array
while (j <= right && nums[i] > 2L * nums[j]) { // Check for reverse pairs
count += mid - i + 1; // Increment count for each reverse pair found
j++; // Move to the next element in the right half
}
}
// Merge step
Arrays.sort(nums, left, right + 1); // Sort the merged array
return count; // Return the count of reverse pairs
}
// Recursive method to divide the array and use mergeAndCount
private int reversePairsRec(int[] nums, int left, int right) {
if (left >= right) return 0; // Base case: if left index is greater than or equal to right index, return 0
int mid = (left + right) / 2; // Calculate the middle index
// Recursively count reverse pairs in left and right halves, and merge
int count =
reversePairsRec(nums, left, mid) + reversePairsRec(nums, mid + 1, right);
count += mergeAndCount(nums, left, mid, right); // Count reverse pairs during merging
return count; // Return total count of reverse pairs
}
// Public method to be called with the array, nums
public int reversePairs(int[] nums) {
return reversePairsRec(nums, 0, nums.length - 1); // Call the recursive method with initial parameters
}
// Main method to test the algorithm with example inputs
public static void main(String[] args) {
Solution solution = new Solution();
// Example 1
int[] nums1 = { 4, 3, 4, 1 };
System.out.println(solution.reversePairs(nums1)); // Expected output: 3
// Example 2
int[] nums2 = { 4, 1, 2 };
System.out.println(solution.reversePairs(nums2)); // Expected output: 1
// Example 3
int[] nums3 = { 6, 5, 4, 3, 2, 1 };
System.out.println(solution.reversePairs(nums3)); // Expected output: 6
}
}
Complexity Analysis
Time Complexity
The time complexity of the algorithm is
- Dividing the array: The array is recursively divided into two halves until each subarray contains a single element, which takes
time considering the depth of the recursion tree. - Counting and merging: During each merge step, the algorithm counts the reverse pairs and then sorts the merged parts. The counting of reverse pairs is done in linear time relative to the size of the subarrays being merged, and sorting is
, where (k) is the number of elements in the subarray. Since the merge happens at every level of the recursion, and considering all levels, it sums up to .
Space Complexity
The space complexity of the solution is
🤖 Don't fully get this? Learn it with Claude
Stuck on Reverse Pairs? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.
Progressively stronger hints — you still solve it.
I'm working on the problem **Reverse Pairs** (DSA). Give me a HINT LADDER: start with the tiniest nudge, then wait. Only reveal the next, stronger hint when I ask. Do NOT show the full solution unless I type 'show solution'. Keep me doing the thinking. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
See the technique, not just code.
Explain the optimal approach to **Reverse Pairs** with a VISUAL walkthrough: trace it on a small concrete example using ASCII art / a step-by-step diagram, narrate what changes each step, then give time & space complexity with a one-line derivation. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Catch bugs, edge cases, sub-optimality.
I'll paste my solution to **Reverse Pairs**. Review it for correctness, missed edge cases, and time/space complexity, then coach me toward the optimal — don't just rewrite it. Ask me to paste my code now. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Lock in recognition with look-alikes.
Give me 2 problems that use the SAME underlying pattern as **Reverse Pairs**. For each, let me attempt first, then review my answer and name the trigger signal that reveals the pattern. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.