hard Reverse Pairs
Problem Statement
Given an integer array nums, return the number of reverse pairs in the array.
A reverse pair is defined as a pair of indices (i, j) such that:
0 <= i < j < nums.lengthnums[i] > 2 * nums[j]
Examples
Example 1
- Input: nums =
[7, 1, 5, 2, 3] - Expected Output:
4 - Justification: The reverse pairs are:
- (0, 1) --> nums[0] = 7, nums[1] = 1, 7 > 2 * 1
- (0, 3) --> nums[0] = 7, nums[3] = 2, 7 > 2 * 2
- (0, 4) --> nums[0] = 7, nums[4] = 3, 7 > 2 * 3
- (2, 3) --> nums[2] = 5, nums[3] = 2, 5 > 2 * 2
Example 2
- Input: nums =
[10, 5, 2, 1] - Expected Output:
4 - Justification: The reverse pairs are:
- (0, 2) --> nums[0] = 10, nums[2] = 2, 10 > 2 * 2
- (0, 3) --> nums[0] = 10, nums[3] = 1, 10 > 2 * 1
- (1, 2) --> nums[1] = 5, nums[2] = 2, 5 > 2 * 2
- (1, 3) --> nums[1] = 5, nums[3] = 1, 5 > 2 * 1
Example 3
- Input: nums =
[7, 3, 2, 5] - Expected Output:
2 - Justification: The reverse pairs are:
- (0, 1) --> nums[0] = 7, nums[1] = 3, 7 > 2 * 3
- (0, 2) --> nums[0] = 7, nums[2] = 2, 7 > 2 * 2
Constraints:
- 1 <= nums.length <= 5 * 104
- -231 <= nums[i] <= 231 - 1
Try it yourself
Try solving this question here:
✅ Solution Reverse Pairs
Problem Statement
Given an integer array nums, return the number of reverse pairs in the array.
A reverse pair is defined as a pair of indices (i, j) such that:
0 <= i < j < nums.lengthnums[i] > 2 * nums[j]
Examples
Example 1
- Input: nums =
[7, 1, 5, 2, 3] - Expected Output:
4 - Justification: The reverse pairs are:
- (0, 1) --> nums[0] = 7, nums[1] = 1, 7 > 2 * 1
- (0, 3) --> nums[0] = 7, nums[3] = 2, 7 > 2 * 2
- (0, 4) --> nums[0] = 7, nums[4] = 3, 7 > 2 * 3
- (2, 3) --> nums[2] = 5, nums[3] = 2, 5 > 2 * 2
Example 2
- Input: nums =
[10, 5, 2, 1] - Expected Output:
4 - Justification: The reverse pairs are:
- (0, 2) --> nums[0] = 10, nums[2] = 2, 10 > 2 * 2
- (0, 3) --> nums[0] = 10, nums[3] = 1, 10 > 2 * 1
- (1, 2) --> nums[1] = 5, nums[2] = 2, 5 > 2 * 2
- (1, 3) --> nums[1] = 5, nums[3] = 1, 5 > 2 * 1
Example 3
- Input: nums =
[7, 3, 2, 5] - Expected Output:
2 - Justification: The reverse pairs are:
- (0, 1) --> nums[0] = 7, nums[1] = 3, 7 > 2 * 3
- (0, 2) --> nums[0] = 7, nums[2] = 2, 7 > 2 * 2
Constraints:
- 1 <= nums.length <= 5 * 104
- -231 <= nums[i] <= 231 - 1
Solution
To solve this problem, we can use the Binary Indexed Tree (BIT) approach, which efficiently handles the problem of counting reverse pairs. The primary challenge is determining how many numbers to the right of each element satisfy the reverse pair condition. By using the BIT, we can efficiently query the count of elements and update the BIT as we iterate through the array. This approach is effective because it reduces the problem's time complexity compared to a brute-force solution, which would be too slow for large inputs.
The BIT approach is particularly well-suited for problems involving range queries and updates, making it ideal for counting conditions like reverse pairs. By leveraging the BIT, we can ensure that the solution is both efficient and scalable.
Step-by-Step Algorithm
-
Initialization:
- Check if the input array
numsis empty. If it is, return 0 as there are no elements to form any pairs. - Create a list named
doubledValuesto store the double of each element innums. This list will help in identifying the reverse pairs by comparing each element with twice the other elements. - Populate the
doubledValueslist by iterating overnumsand multiplying each element by 2. Store these values in the list.
- Check if the input array
-
Create a Set of Unique Numbers:
- Create a
TreeSetnameduniqueNumbers. ATreeSetautomatically sorts and removes duplicates, ensuring that all the elements in the set are unique and in ascending order. - Add all elements from
numstouniqueNumbers. - Add all elements from
doubledValuestouniqueNumbers. This ensures that both the original elements and their doubled values are included in the sorted set.
- Create a
-
Mapping Each Unique Number to a Rank:
- Create a
HashMapnamedrankMapto store the rank of each unique number. The rank of a number is its position in the sorted order ofuniqueNumbers. - Initialize a variable
rankto 0. - Iterate over each number in
uniqueNumbers. For each number, incrementrankby 1 and store this rank in therankMapwith the number as the key.
- Create a
-
Initialize Fenwick Tree:
- Create an instance of
FenwickTreenamedfenwickTree. The size of the Fenwick Tree is set to the size ofrankMap, which is the total number of unique elements (including doubled values). - The Fenwick Tree is used to keep track of the frequency of ranks and efficiently query the number of elements smaller than a given rank.
- Create an instance of
-
Count Reverse Pairs:
- Initialize a variable
reversePairCountto 0. This variable will store the final count of reverse pairs. - Iterate over the array
numsin reverse order (from the last element to the first).- For each element
nums[i], query the Fenwick Tree to find how many elements with a rank less thanrankMap.get(nums[i])exist. This represents the number of validjindices for which the conditionnums[i] > 2 * nums[j]holds true. - Add the result of the query to
reversePairCount. - Update the Fenwick Tree by inserting the rank of the doubled value of
nums[i]. This prepares the tree for the next iteration, ensuring that future queries will correctly count all valid pairs.
- For each element
- Initialize a variable
-
Return the Result:
- After the loop completes,
reversePairCountwill contain the total number of reverse pairs in the array. Return this value as the final result.
- After the loop completes,
Algorithm Walkthrough
Input: nums = [7, 1, 5, 2, 3]
-
Initialization:
nums = [7, 1, 5, 2, 3]doubledValuesafter processingnumswill be[14, 2, 10, 4, 6].
-
Create a Set of Unique Numbers:
uniqueNumberswill be created by adding elements fromnumsanddoubledValues. The resulting set will be{1, 2, 3, 4, 5, 6, 7, 10, 14}.
-
Mapping Each Unique Number to a Rank:
rankMapwill be created by assigning ranks as follows:1 -> 12 -> 23 -> 34 -> 45 -> 56 -> 67 -> 710 -> 814 -> 9
-
Initialize Fenwick Tree:
- A
FenwickTreeof size 9 (size ofrankMap) is initialized.
- A
-
Iteration 1 (i = 4, nums[4] = 3):
- Query Fenwick Tree for ranks less than
3 (rank 3):- No elements are found (
query(2)returns 0).
- No elements are found (
- Update Fenwick Tree with
rankMap.get(6)(doubled value of3, rank 6):- Tree state after update:
[0, 0, 0, 0, 0, 0, 1, 0, 1, 0]
- Tree state after update:
- Query Fenwick Tree for ranks less than
-
Iteration 2 (i = 3, nums[3] = 2):
- Query Fenwick Tree for ranks less than
2 (rank 2):- No elements are found (
query(1)returns 0).
- No elements are found (
- Update Fenwick Tree with
rankMap.get(4)(doubled value of2, rank 4):- Tree state after update:
[0, 0, 0, 0, 1, 0, 1, 0, 2, 0]
- Tree state after update:
- Query Fenwick Tree for ranks less than
-
Iteration 3 (i = 2, nums[2] = 5):
- Query Fenwick Tree for ranks less than
5 (rank 5):- One element is found (
query(4)returns 1). - Add
1toreversePairCount. Now,reversePairCount= 1.
- One element is found (
- Update Fenwick Tree with
rankMap.get(10)(doubled value of5, rank 8):- Tree state after update:
[0, 0, 0, 0, 1, 0, 1, 0, 3, 0]
- Tree state after update:
- Query Fenwick Tree for ranks less than
-
Iteration 4 (i = 1, nums[1] = 1):
- Query Fenwick Tree for ranks less than
1 (rank 1):- No elements are found (
query(0)returns 0).
- No elements are found (
- Update Fenwick Tree with
rankMap.get(2)(doubled value of1, rank 2):- Tree state after update:
[0, 0, 1, 0, 2, 0, 1, 0, 4, 0]
- Tree state after update:
- Query Fenwick Tree for ranks less than
-
Iteration 5 (i = 0, nums[0] = 7):
- Query Fenwick Tree for ranks less than
7 (rank 7):- Three elements are found (
query(6)returns 3). - Add
3toreversePairCount. Now,reversePairCount= 3.
- Three elements are found (
- Update Fenwick Tree with
rankMap.get(14)(doubled value of7, rank 9):- Tree state after update:
[0, 0, 1, 0, 2, 0, 1, 0, 4, 1]
- Tree state after update:
- Query Fenwick Tree for ranks less than
After all iterations, the final reversePairCount is 4.
Code
import java.util.*;
class FenwickTree {
private int[] sums;
// Constructor to initialize the Fenwick Tree
public FenwickTree(int n) {
sums = new int[n + 1];
}
// Method to update the Fenwick Tree
public void update(int index, int value) {
while (index < sums.length) {
sums[index] += value;
index += lowbit(index); // Move to the next node
}
}
// Method to query the cumulative frequency up to index
public int query(int index) {
int sum = 0;
while (index > 0) {
sum += sums[index];
index -= lowbit(index); // Move to the parent node
}
return sum;
}
// Helper method to get the lowest bit
private int lowbit(int x) {
return x & (-x);
}
}
public class Solution {
public int reversePairs(int[] nums) {
int n = nums.length;
if (n == 0) return 0;
// Create a list to store double values of each element in nums
List<Long> doubledValues = new ArrayList<>();
for (int num : nums) {
doubledValues.add(2L * num);
}
// Create a sorted set to remove duplicates and get a sorted list of numbers
Set<Long> uniqueNumbers = new TreeSet<>();
for (int num : nums) uniqueNumbers.add((long) num);
uniqueNumbers.addAll(doubledValues);
// Create a rank map to map each unique number to its rank
Map<Long, Integer> rankMap = new HashMap<>();
int rank = 0;
for (long num : uniqueNumbers) {
rankMap.put(num, ++rank);
}
// Initialize the Fenwick Tree with the size of unique numbers
FenwickTree fenwickTree = new FenwickTree(rankMap.size());
int reversePairCount = 0;
for (int i = n - 1; i >= 0; i--) {
// Query for the count of elements less than nums[i] / 2
reversePairCount += fenwickTree.query(rankMap.get((long) nums[i]) - 1);
// Update the Fenwick Tree with the rank of doubled value of nums[i]
fenwickTree.update(rankMap.get(2L * nums[i]), 1);
}
return reversePairCount;
}
public static void main(String[] args) {
Solution solution = new Solution();
// Example 1
int[] nums1 = { 7, 1, 5, 2, 3 };
int expectedOutput1 = 4;
System.out.println("Input: " + Arrays.toString(nums1));
System.out.println("Expected Output: " + expectedOutput1);
System.out.println("Actual Output: " + solution.reversePairs(nums1));
// Example 2
int[] nums2 = { 10, 5, 2, 1 };
int expectedOutput2 = 4;
System.out.println("Input: " + Arrays.toString(nums2));
System.out.println("Expected Output: " + expectedOutput2);
System.out.println("Actual Output: " + solution.reversePairs(nums2));
// Example 3
int[] nums3 = { 7, 3, 2, 5 };
int expectedOutput3 = 2;
System.out.println("Input: " + Arrays.toString(nums3));
System.out.println("Expected Output: " + expectedOutput3);
System.out.println("Actual Output: " + solution.reversePairs(nums3));
}
}
Complexity Analysis
Time Complexity
-
Sorting and Ranking:
- The process of creating the sorted set of unique numbers takes
time, where (n) is the size of the input array. This includes both the insertion into the set and the construction of the rank map. - Mapping the numbers to their ranks takes
time.
- The process of creating the sorted set of unique numbers takes
-
Fenwick Tree Operations:
- Each
updateandqueryoperation in the Fenwick Tree takestime, where (n) is the size of the rank map. Since we perform these operations for each element in the array, the time complexity is .
Combining these, the overall time complexity of the algorithm is
. - Each
Space Complexity
-
Fenwick Tree:
- The space required by the Fenwick Tree is
, where (n) is the number of unique elements in the input array (including the doubled values).
- The space required by the Fenwick Tree is
-
Additional Storage:
- We use additional space for storing the rank map and the doubled values list. Both require
space.
Therefore, the overall space complexity of the algorithm is
. - We use additional space for storing the rank map and the doubled values list. Both require
🤖 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.