CLOSE
🛠️ Settings

Problem Statement

Given an integer array nums. Return the number of reverse pairs in the array.

An index pair (i, j) is called a reverse pair if:

  • 0 <= i < j < nums.length
  • nums[i] > 2 * nums[j].

LeetCode:

Constraints:

1 <= nums.length <= 5 * 10^4
-2^31 <= nums[i] <= 2^31 - 1
  • 1 ≤ nums.length ≤ 5*10^4
    • The input array will have at least 1 element.
    • At most, it can have 50,000 elements.
    • Any algorithm worse than O(n log n) (like O(n^2)) might time out for large inputs, so you need an efficient approach.
  • -2^31 ≤ nums[i] ≤ 2^31 - 1
    • This referes to the value range of each element in the array:
      • The minimum possible value is:
        • -2^31 = -2147483648.
        • 2^31 - 1 = 2147483647.
      • These are limits of a 32-bit signed integer.
      • If you ever do operations like 2 * nums[i], there is a risk of integer overflow because:

        int x = 1e9;
        int y = 2 * x; // = 2e9, still OK
        int z = 2 * INT_MAX; // OVERFLOW
        

Examples

Example: 1

Input: nums = [6, 4, 1, 2, 7]
Output: 3

Explanation:
The reverse pairs are:
(0, 2): nums[0] = 6, nums[2] = 1, 6 > 2 * 1
(0, 3): nums[0] = 6, nums[3] = 2, 6 > 2 * 2
(1, 2): nums[1] = 4, nums[2] = 1, 4 > 2 * 1
Example: 2

Input: nums = [5, 4, 4, 3, 3]
Output: 0

Explanation:
No pairs satisfy both the conditions.
Example: 3

Input: nums = [6, 4, 4, 2, 2]
Output: 2

Explanation:
The reverse pairs are:
(0, 3): nums[0] = 6, nums[3] = 2, 6 > 2 * 2
(0, 3): nums[0] = 6, nums[4] = 2, 6 > 2 * 2

Different Approaches

1️⃣ Brute Force Approach

Intuition:

The straightforward approach to solve this problem is to iterate through each element in the array and run an inner loop say(j) to check all subsequent elements arr[j], if the condition arr[i] > 2 x arr[j] holds true, where i is the parent loop, then it is a reverse pair otherwise it's not a reverse pair.

Approach:

  1. Iterate in the array from 0 to N-1 to select the arr[i]. As index j should be greater than index i, inside loop i, run another loop i.e. j from i+1 to N-1, and select the element arr[j].
  2. Inside this second loop, check if arr[i] is greater than 2*arr[j] i.e. if arr[i] and arr[j] can be a pair. If they satisfy the condition, increase the count by 1. Finally, return the count as our answer.

Code:

#include <bits/stdc++.h>
using namespace std;

class Solution {
public:
    /* Function to count reverse
    pairs where a[i] > 2 * a[j]*/
    int reversePairs(vector<int>& nums) {
        
        // Call countPairs with the vector and its size
        return countPairs(nums, nums.size()); 
        
    }

private:
    /* Helper function to count pairs
    satisfying the condition a[i] > 2 * a[j]*/
    int countPairs(vector<int>& nums, int n) {
        
        // Initialize count of reverse pairs
        int cnt = 0;
        
        /* Nested loops to check each
        pair (i, j) where i < j*/
        for (int i = 0; i < n; i++) {
            
            for (int j = i + 1; j < n; j++) {
                
                /* Check if the condition 
                a[i] > 2 * a[j] holds*/
                if (nums[i] > 2 * nums[j]) {
                    
                    /* Increment count if
                    condition is satisfied*/
                    cnt++; 
                    
                }
            }
        }
        // Return the total count of reverse pairs
        return cnt; 
    }
};

int main() {
    
    vector<int> nums = {6, 4, 1, 2, 7}; 
    
    // Create an instance of the Solution class
    Solution sol; 
    
    int cnt = sol.reversePairs(nums); 
    
    // Output the result
    cout << "The number of reverse pairs is: " << cnt << endl;
    return 0; 
}

Complexity Analysis:

  • Time Complexity: O(N^2), where N is size of the given array. For using nested loops here and those two loops roughly run for N times.
  • Space Complexity: O(1), no extra space is used to solve this problem.

2️⃣ Modified Merge Sort

Intuition:

In order to solve this problem in optimal way, use the concept of modified merge sort. Here, the approach will be to check, for every element in the sorted left, how many elements in the right half (also sorted) can make pair.

Let's understand:

       +-----+-----+-----+-----+
arr1 = |  6  | 13  | 21  | 25  |
       +-----+-----+-----+-----+
          0     1     2     3
          ^
          |
          i

       +-----+-----+-----+-----+-----+-----+
arr2 = |  1  |  2  |  3  |  4  |  4  |  8  |
       +-----+-----+-----+-----+-----+-----+
          0     1     2     3     4     5
                      ^
                      |
                      j
  
  Here, the checking ends at j = 2,
        as 6 = 2 * 3

For the first element of the left half i.e. 6, start checking from index 0 of the right half i.e. arr2[]. Now, we can clearly see that the first two elements of arr2[] can make a pair with arr1[0] i.e. 6.

       +-----+-----+-----+-----+
arr1 = |  6  | 13  | 21  | 25  |
       +-----+-----+-----+-----+
          0     1     2     3
                ^
                |
                i

       +-----+-----+-----+-----+-----+-----+
arr2 = |  1  |  2  |  3  |  4  |  4  |  8  |
       +-----+-----+-----+-----+-----+-----+
          0     1     2     3     4     5
                      ^
                      |
                      j
           For element 13 we will start checking
           from this index.

This process will work because arr1[1] will always be greater than arr1[0] which concludes if arr2[0] and arr2[1] are making a pair with arr1[0], they will obviously make pairs with a number greater than arr1[0] i.e. arr1[1].

Thus before the merge step in the merge sort algorithm, calculate the total number of pairs each time.

Code:

/*

    Time Complexity : O(NlogN), Each recursive call to performs two recursive calls on subslices of size N/2 and
    One linear scans of length <= N. Therefore, the time complexity of the divide & conquer approach can be
    represented by the following recurrence relation: T(N)=2T(N/2)+N. Where N is the size of the Array(nums).

    Space Complexity : O(N), Recursion Stack Space O(logN) + Array(temp) space O(N). 

    Solved using Array + Divide and Conquer + Merge Sort. Optimized Approach.

*/


/************* Approach 2 *****************/

class Solution {
private: 
    void merge(vector<int>& nums, int low, int mid, int high, int& reversePairsCount){
        int j = mid+1;
        for(int i=low; i<=mid; i++){
            while(j<=high && nums[i] > 2*(long long)nums[j]){
                j++;
            }
            reversePairsCount += j-(mid+1);
        }
        int size = high-low+1;
        vector<int> temp(size, 0);
        int left = low, right = mid+1, k=0;
        while(left<=mid && right<=high){
            if(nums[left] < nums[right]){
                temp[k++] = nums[left++];
            }
            else{
                temp[k++] = nums[right++];
            }
        }
        while(left<=mid){
            temp[k++] = nums[left++]; 
        }
        while(right<=high){
            temp[k++] = nums[right++]; 
        }
        int m=0;
        for(int i=low; i<=high; i++){
            nums[i] = temp[m++];
        }
    }

    void mergeSort(vector<int>& nums, int low, int high, int& reversePairsCount){
        if(low >= high){
            return;
        }
        int mid = (low + high) >> 1;
        mergeSort(nums, low, mid, reversePairsCount);
        mergeSort(nums, mid+1, high, reversePairsCount);
        merge(nums, low, mid, high, reversePairsCount);
    }
public:
    int reversePairs(vector<int>& nums) {
        int reversePairsCount = 0;
        mergeSort(nums, 0, nums.size()-1, reversePairsCount);
        return reversePairsCount;
    }
};
#include <bits/stdc++.h>
using namespace std;

class Solution {
public:
    /* Function to count reverse
    pairs where a[i] > 2 * a[j]*/
    int reversePairs(vector<int>& nums) {
        int n = nums.size();
        return team(nums, n);
    }

private:
    /* Merge function to merge two 
    sorted halves and count reverse pairs*/
    void merge(vector<int>& arr, int low, int mid, int high) {
        // temporary array
        vector<int> temp; 
        
        // starting index of left half of arr
        int left = low;  
        
        // starting index of right half of arr
        int right = mid + 1; 

        // Merge and count reverse pairs
        while (left <= mid && right <= high) {
            
            if (arr[left] <= arr[right]) {
                temp.push_back(arr[left]);
                left++;
            } 
            else {
                temp.push_back(arr[right]);
                right++;
                
            }
        }

        // Copy remaining elements from left half
        while (left <= mid) {
            temp.push_back(arr[left]);
            left++;
        }

        // Copy remaining elements from right half
        while (right <= high) {
            temp.push_back(arr[right]);
            right++;
        }

        // Transfer sorted elements from temp to arr
        for (int i = low; i <= high; i++) {
            arr[i] = temp[i - low];
        }
    }
private:
    //Function to count reverse pairs
    int countPairs(vector<int> &arr, int low, int mid, int high) {
        int right = mid + 1;
        int cnt = 0;
        
        for (int i = low; i <= mid; i++) {
            
            /*while right is less than equal to high 
           and arr[i] is greater than 2 * arr[right] 
           then increment right by 1*/
            while (right <= high && arr[i] > 2 * arr[right]) right++;
            
            cnt += (right - (mid + 1));
        }
        //Return the count
        return cnt;
    }
private:
    /* Merge sort function to sort the
    array and count reverse pairs*/
    int mergeSort(vector<int>& arr, int low, int high) {
        int cnt = 0;
        
        if(low >= high){
            return cnt;
        }
        int mid = low + (high - low) / 2;
            
        // Sort left half
        cnt += mergeSort(arr, low, mid); 
            
        // Sort right half
        cnt += mergeSort(arr, mid + 1, high);
        
        cnt += countPairs(arr, low, mid, high);
            
        // Merge and count pairs
        merge(arr, low, mid, high); 
        
        //Return the count of reverse pairs
        return cnt;
    }
private:
    int team(vector <int> & skill, int n){
        return mergeSort(skill, 0, n - 1);
    }
};

int main() {
    vector<int> nums = {6, 4, 1, 2, 7};
    
    //Create an instance of Solution class
    Solution sol;
    
    int cnt = sol.reversePairs(nums);
    
    //Print the count of reverse pairs
    cout << "The number of reverse pairs is: " << cnt << endl;
    return 0;
}

Complexity Analysis:

  • Time Complexity: O(2N * logN), where N is size of the given array.
    • Inside the mergeSort() we call merge() and countPairs() except mergeSort() itself. Now, inside the function countPairs(), though we are running a nested loop, we are actually iterating the left half once and the right half once in total.
    • That is why, the time complexity is O(N). And the merge() function also takes O(N). The mergeSort() takes O(logN) time complexity. Therefore, the overall time complexity will be O(logN x (N+N)) = O(2NxlogN)
  • Space Complexity: O(N), as in the merge sort, a temporary array to store elements in sorted order is used.