Open In App

Subarray Inversions

Last Updated : 22 Mar, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

We have an array A of n integers that we’re planning on sorting. Specifically, we want to know how close the array is to sorted. In order to achieve this, we introduce the idea of an inversion. An inversion in array is a pair of two indices i and j such that i < j and A[i] > A[j]. If our array contains one inversion, we need only swap A[i] and A[j] in order to sort the array. An array that contains 0 inversions is by definition sorted. 

Problem: 

Given an array A of n integers, find the sum of the number of inversions in all subarrays of length k. To clarify, one must determine the number of inversions in each of the n – k + 1 subarrays of length k and add them together.
Input: The first line contains two space separated integers n and k. The next line contains a sequence of n space separated integers where the ith integer in the sequence is A[i].

Examples: 

Input : arr[] = {1 2 3 4 5 6}
        k = 2
Output : 0

Input : arr[] = {1 6 7 2}
        k = 3
Output : 2
There are two subarrays of size 3,
{1, 6, 7} and {6, 7, 2}. Count of
inversions in first subarray is 0
and count of inversions in second
subarray is 2. So sum is 0 + 2 = 2

Input :  8 4
12 3 14 8 15 1 4 22
Output : 14

Input :  10 5
15 51 44 44 76 50 29 88 48 50
Output : 25

[1] Naive Approach: This problem seems fairly trivial at first, and we can easily implement a naive algorithm to brute force the solution. We simply create a window of length k and roll the window along A, adding the number of inversions in the window at each iteration. To find the number of inversions, the simplest approach is to use two for loops to consider all pairs of elements and increment a counter if the pair forms an inversion. 

This approach is very easy to implement, but is it efficient? Let’s analyze the algorithm. The outermost loop runs n – k + 1 times, once for each k-subarray of A. At each of these iterations, we find the number of inversions in the window. To do this, we consider element 1 and elements 2, …, n, then element 2 and elements 3, …, n until element n – 1 and element n. Effectively, we’re performing n + (n – 1) + (n – 2) + … + 1 = n(n + 1)/2 operations. Thus, our algorithm performs approximately (n – k + 1)(n)(n + 1)/2 operations which is O(n^3 – kn^2). Since k can range from 1 to n, the worst case performance for this algorithm is O(n^3) when k = n/2. We can do better! 

Implementation:

C++




// C++ implementation of above approach
#include <iostream>
using namespace std;
 
// Helper function, counts number of inversions
// via bubble sort loop
int bubble_count(int arr[], int start, int end)
{
    int count = 0;
    for (int i = start; i < end; i++)
    {
        for (int j = i + 1; j < end; j++)
        {
            if (arr[i] > arr[j])
            {
                count++;
            }
        }
    }
    return count;
}
 
// Inversion counting method, slides window of
// [start, end] across array
int inversion_count(int n, int k, int a[])
{
    int count = 0;
    for (int start = 0; start < n - k + 1; start++)
    {
        int end = start + k;
        count += bubble_count(a, start, end);
    }
    return count;
}
 
// Driver Code
int main()
{
    int n = 10;
    int arr[n] = { 15, 51, 44, 44, 76,
                   50, 29, 88, 48, 50 };
    int k = 5;
     
    int result = inversion_count(n, k, arr);
    cout << result;
    return 0;
}
 
// This code is contributed by PrinciRaj1992


Java




public class Subarray_Inversions {
 
    // Inversion counting method, slides window of [start,
    // end] across array
    static int inversion_count(int n, int k, int[] a)
    {
        int count = 0;
        for (int start = 0; start < n - k + 1; start++) {
            int end = start + k;
            count += bubble_count(a, start, end);
        }
        return count;
    }
 
    // Helper function, counts number of inversions via
    // bubble sort loop
    public static int bubble_count(int[] arr, int start, int end)
    {
        int count = 0;
        for (int i = start; i < end; i++) {
            for (int j = i + 1; j < end; j++) {
                if (arr[i] > arr[j]) {
                    count++;
                }
            }
        }
        return count;
    }
 
    public static void main(String[] args)
    {
        int n = 10;
        int[] arr = { 15, 51, 44, 44, 76, 50, 29, 88, 48, 50 };
        int k = 5;
 
        long result = inversion_count(n, k, arr);
        System.out.println(result);
    }
}


Python3




# Python3 implementation of above approach
 
# Helper function, counts number of inversions
# via bubble sort loop
def bubble_count(arr, start, end):
    count = 0;
    for i in range(start, end):
 
        for j in range(i + 1, end):
 
            if (arr[i] > arr[j]):
                count += 1;
 
    return count;
 
# Inversion counting method, slides window
# of [start, end] across array
def inversion_count(n, k, a):
    count = 0;
    for start in range(0, n - k + 1):
        end = start + k;
        count += bubble_count(a, start, end);
 
    return count;
 
# Driver Code
if __name__ == '__main__':
    n = 10;
    arr = [15, 51, 44, 44, 76,
           50, 29, 88, 48, 50];
    k = 5;
 
    result = inversion_count(n, k, arr);
    print(result);
 
# This code is contributed by Rajput-Ji


C#




// C# implementation of above approach
using System;
 
public class Subarray_Inversions
{
 
    // Inversion counting method, slides window of [start,
    // end] across array
    static int inversion_count(int n, int k, int[] a)
    {
        int count = 0;
        for (int start = 0; start < n - k + 1; start++)
        {
            int end = start + k;
            count += bubble_count(a, start, end);
        }
        return count;
    }
 
    // Helper function, counts number of inversions via
    // bubble sort loop
    public static int bubble_count(int[] arr, int start, int end)
    {
        int count = 0;
        for (int i = start; i < end; i++)
        {
            for (int j = i + 1; j < end; j++)
            {
                if (arr[i] > arr[j])
                {
                    count++;
                }
            }
        }
        return count;
    }
 
    // Driver code
    public static void Main()
    {
        int n = 10;
        int[] arr = { 15, 51, 44, 44, 76, 50, 29, 88, 48, 50 };
        int k = 5;
 
        long result = inversion_count(n, k, arr);
        Console.WriteLine(result);
    }
}
 
// This code is contributed by Rajput-Ji


Javascript




<script>
 
    // Inversion counting method, slides window of [start,
    // end] across array
    function inversion_count(n,k,a)
    {
        let count = 0;
        for (let start = 0; start < n - k + 1; start++) {
            let end = start + k;
            count += bubble_count(a, start, end);
        }
        return count;
    }
 
    // Helper function, counts number of inversions via
    // bubble sort loop
    function bubble_count(arr,start,end)
    {
        let count = 0;
        for (let i = start; i < end; i++) {
            for (let j = i + 1; j < end; j++) {
                if (arr[i] > arr[j]) {
                    count++;
                }
            }
        }
        return count;
    }
 
     
        let n = 10;
        let arr = [ 15, 51, 44, 44, 76, 50, 29, 88, 48, 50 ];
        let k = 5;
 
        let result = inversion_count(n, k, arr);
        document.write(result);
 
// This code is contributed by G.Sravan Kumar (171FA07058)
 
</script>


Output

25

[2] Mergesort-based Implementation: One optimization we can make is improving our inefficient, quadratic-time inversion counting method. One approach could involve using a mergesort-based method as outlined in this article. Since this runs in O(nlogn), our overall runtime is reduced to O(n^2logn), which is better, but still won’t be able to handle cases for which n = 10^6 for instance.  

Implementation:

C++




#include <bits/stdc++.h>
using namespace std;
 
// Counts number of inversions when merging
int merge_inversion_count(vector<int>& arr,
                          vector<int> left,
                          vector<int> right)
{
  int x = left.size();
  int y = right.size();
  int i = 0, j = 0, count = 0;
  while (i < x || j < y) {
    if (i == x) {
      arr[i + j] = right[j];
      j++;
    }
    else if (j == y) {
      arr[i + j] = left[i];
      i++;
    }
    else if (left[i] <= right[j]) {
      arr[i + j] = left[i];
      i++;
    }
    else {
      arr[i + j] = right[j];
      count += x - i;
      j++;
    }
  }
  return count;
}
 
// Divide and conquer approach -- splits array and counts
// inversions via merge method
int subarray_inversion_count(vector<int>& arr)
{
  int n = arr.size();
  if (n < 2)
    return 0;
 
  int m = (n + 1) / 2;
 
  vector<int> left;
  for (int i = 0; i < m; i++)
    left.push_back(arr[i]);
  vector<int> right;
  for (int i = m; i < n; i++)
    right.push_back(arr[i]);
 
  return subarray_inversion_count(left)
    + subarray_inversion_count(right)
    + merge_inversion_count(arr, left, right);
}
// Inversion counting method, slides window of [start,
// end] across array
int inversion_count(int n, int k, vector<int>& a)
{
  int count = 0;
  for (int start = 0; start < n - k + 1; start++) {
    vector<int> sub_array(k);
    for (int i = start; i < start + k; i++) {
      sub_array[i - start] = a[i];
    }
    count += subarray_inversion_count(sub_array);
    // cout<<count<<endl;
  }
  return count;
}
 
int main()
{
  int n = 10;
  vector<int> arr{
    15, 51, 44, 44, 76, 50, 29, 88, 48, 50
    };
  int k = 5;
 
  int result = inversion_count(n, k, arr);
  cout << (result) << endl;
}
 
// This code is contributed by garg28harsh.


Java




import java.util.*;
 
public class Subarray_Inversions {
 
    // Inversion counting method, slides window of [start,
    // end] across array
    static int inversion_count(int n, int k, int[] a)
    {
        int count = 0;
        for (int start = 0; start < n - k + 1; start++) {
            int[] sub_array = new int[k];
            for (int i = start; i < start + k; i++) {
                sub_array[i - start] = a[i];
            }
            count += subarray_inversion_count(sub_array);
        }
        return count;
    }
 
    // Counts number of inversions when merging
    public static long merge_inversion_count(int[] arr,
                                int[] left, int[] right)
    {
        int i = 0, j = 0, count = 0;
        while (i < left.length || j < right.length) {
            if (i == left.length) {
                arr[i + j] = right[j];
                j++;
            } else if (j == right.length) {
                arr[i + j] = left[i];
                i++;
            } else if (left[i] <= right[j]) {
                arr[i + j] = left[i];
                i++;
            } else {
                arr[i + j] = right[j];
                count += left.length - i;
                j++;
            }
        }
        return count;
    }
 
    // Divide and conquer approach -- splits array and counts
    // inversions via merge method
    public static long subarray_inversion_count(int[] arr)
    {
        if (arr.length < 2)
            return 0;
 
        int m = (arr.length + 1) / 2;
        int left[] = Arrays.copyOfRange(arr, 0, m);
        int right[] = Arrays.copyOfRange(arr, m, arr.length);
 
        return subarray_inversion_count(left) +
               subarray_inversion_count(right) +
               merge_inversion_count(arr, left, right);
    }
 
    public static void main(String[] args)
    {
        int n = 10;
        int[] arr = { 15, 51, 44, 44, 76, 50, 29, 88, 48, 50 };
        int k = 5;
 
        long result = inversion_count(n, k, arr);
        System.out.println(result);
    }
}


Python3




class Subarray_Inversions :
    # Inversion counting method, slides window of [start,
    # end] across array
    @staticmethod
    def  inversion_count( n,  k,  a) :
        count = 0
        start = 0
        while (start < n - k + 1) :
            sub_array = [0] * (k)
            i = start
            while (i < start + k) :
                sub_array[i - start] = a[i]
                i += 1
            count += Subarray_Inversions.subarray_inversion_count(sub_array)
            start += 1
        return count
    # Counts number of inversions when merging
    @staticmethod
    def  merge_inversion_count( arr,  left,  right) :
        i = 0
        j = 0
        count = 0
        while (i < len(left) or j < len(right)) :
            if (i == len(left)) :
                arr[i + j] = right[j]
                j += 1
            elif(j == len(right)) :
                arr[i + j] = left[i]
                i += 1
            elif(left[i] <= right[j]) :
                arr[i + j] = left[i]
                i += 1
            else :
                arr[i + j] = right[j]
                count += len(left) - i
                j += 1
        return count
    # Divide and conquer approach -- splits array and counts
    # inversions via merge method
    @staticmethod
    def  subarray_inversion_count( arr) :
        if (len(arr) < 2) :
            return 0
        m = int((len(arr) + 1) / 2)
        left = arr[0:m]
        right = arr[m:len(arr)]
        return Subarray_Inversions.subarray_inversion_count(left) + Subarray_Inversions.subarray_inversion_count(right) + Subarray_Inversions.merge_inversion_count(arr, left, right)
    @staticmethod
    def main( args) :
        n = 10
        arr = [15, 51, 44, 44, 76, 50, 29, 88, 48, 50]
        k = 5
        result = Subarray_Inversions.inversion_count(n, k, arr)
        print(result)
     
 
if __name__=="__main__":
    Subarray_Inversions.main([])
     
    # This code is contributed by aadityaburujwale.


C#




using System;
using System.Collections.Generic;
 
  
public class Subarray_Inversions {
  
    // Inversion counting method, slides window of [start,
    // end] across array
    static int inversion_count(int n, int k, int[] a)
    {
        int count = 0;
        for (int start = 0; start < n - k + 1; start++) {
            int[] sub_array = new int[k];
            for (int i = start; i < start + k; i++) {
                sub_array[i - start] = a[i];
            }
            count += subarray_inversion_count(sub_array);
        }
        return count;
    }
  
    // Counts number of inversions when merging
    public static int merge_inversion_count(int[] arr,
                                int[] left, int[] right)
    {
        int i = 0, j = 0, count = 0;
        while (i < left.Length || j < right.Length) {
            if (i == left.Length) {
                arr[i + j] = right[j];
                j++;
            } else if (j == right.Length) {
                arr[i + j] = left[i];
                i++;
            } else if (left[i] <= right[j]) {
                arr[i + j] = left[i];
                i++;
            } else {
                arr[i + j] = right[j];
                count += left.Length - i;
                j++;
            }
        }
        return count;
    }
  
    // Divide and conquer approach -- splits array and counts
    // inversions via merge method
    public static int subarray_inversion_count(int[] arr)
    {
        if (arr.Length < 2)
            return 0;
  
        int m = (arr.Length + 1) / 2;
        int []left = new int[m];
        Array.Copy(arr, 0, left,0, m);
        int []right = new int[arr.Length - m];
        Array.Copy(arr, m, right,0, arr.Length - m);
  
        return subarray_inversion_count(left) +
               subarray_inversion_count(right) +
               merge_inversion_count(arr, left, right);
    }
  
    public static void Main(String[] args)
    {
        int n = 10;
        int[] arr = { 15, 51, 44, 44, 76, 50, 29, 88, 48, 50 };
        int k = 5;
  
        long result = inversion_count(n, k, arr);
        Console.WriteLine(result);
    }
}
 
// This code is contributed by Rajput-Ji


Javascript




// Counts number of inversions when merging
function merge_inversion_count(arr, left, right) {
  let x = left.length;
  let y = right.length;
  let i = 0, j = 0, count = 0;
  while (i < x || j < y) {
    if (i == x) {
      arr[i + j] = right[j];
      j++;
    }
    else if (j == y) {
      arr[i + j] = left[i];
      i++;
    }
    else if (left[i] <= right[j]) {
      arr[i + j] = left[i];
      i++;
    }
    else {
      arr[i + j] = right[j];
      count += x - i;
      j++;
    }
  }
  return count;
}
 
// Divide and conquer approach -- splits array and counts
// inversions via merge method
function subarray_inversion_count(arr) {
  let n = arr.length;
  if (n < 2)
    return 0;
 
  let m = Math.floor((n + 1) / 2);
 
  let left = [];
  for (let i = 0; i < m; i++)
    left.push(arr[i]);
  let right = [];
  for (let i = m; i < n; i++)
    right.push(arr[i]);
 
  return subarray_inversion_count(left)
    + subarray_inversion_count(right)
    + merge_inversion_count(arr, left, right);
}
 
// Inversion counting method, slides window of [start,
// end] across array
function inversion_count(n, k, a) {
  let count = 0;
  for (let start = 0; start < n - k + 1; start++) {
    let sub_array = [];
    for (let i = 0; i < k; i++) {
      sub_array.push(0);
    }
    for (let i = start; i < start + k; i++) {
      sub_array[i - start] = a[i];
    }
    count += subarray_inversion_count(sub_array);
    // cout<<count<<endl;
  }
  return count;
}
 
let n = 10;
let arr = [15, 51, 44, 44, 76, 50, 29, 88, 48, 50];
let k = 5;
 
let result = inversion_count(n, k, arr);
console.log(result);
 
// This code is contributed by akashish__


Output

25

[3] Overlapping Subarrays Implementation: Let’s revisit our overall approach. We’re looking at the window [0, k) and finding the number of inversions, then we look at [1, k+1). There’s a significant overlap in this range: we’ve already counted the number of inversions in [1, k) during the first iteration, and now we’re counting them again! Instead of counting inversions, let’s count the change in inversions from one window to the next. Effectively, shifting the window is just adding one element to the head of the window and removing an element from its tail — the body of the window remains the same. Checking for inversions among internal elements would be redundant; all we need to do is add the number of inversions induced by the new element and subtract the number of inversions induced by the removed element. We now only need to count the number of inversions in the first window, which we can do in O(klogk) time, and for each of the n – k additional windows, we simply perform one iteration through the k elements in the array to find the change in the number of inversions. Our overall runtime is now O(k(n – k) + klogk) = O(nk – k) which is still worst case O(n^2)

Implementation:

C++




// C++ code for the above approach
#include <bits/stdc++.h>
using namespace std;
 
long inversion_count(int n, int m, int arr[]);
void subarray_inversion_count(int arr[], int start, int end,
                              long subarray_count,
                              long ans[]);
long merge_inversion_count(int arr[], int left[],
                           int right[], int nL, int nR);
long subarray_inversion_count_initial(int arr[], int n);
 
// Inversion counting method, slides window of [start,
// end] across array
long inversion_count(int n, int m, int arr[])
{
    long count = 0;
    int array1[m];
    copy(arr, arr + m, array1);
    count += subarray_inversion_count_initial(array1, m);
    int array2[m - 1];
    copy(arr + 1, arr + m, array2);
    long subarray_count
        = subarray_inversion_count_initial(array2, m - 1);
    for (int start = 1; start <= n - m; start++) {
        int end = start + m - 1;
        long ans[2];
        subarray_inversion_count(arr, start, end,
                                 subarray_count, ans);
        count += ans[0];
        subarray_count = ans[1];
    }
    return count;
}
 
// start >=1; find inversion in interval [start, end)
void subarray_inversion_count(int arr[], int start, int end,
                              long subarray_count,
                              long ans[])
{
    int new_element = arr[end];
    long count = subarray_count;
    for (int i = start; i < end; i++) {
        if (new_element < arr[i])
            count++;
    }
    long totalSum = count;
    int last_element = arr[start];
    for (int i = start + 1; i <= end; i++) {
        if (last_element > arr[i])
            count--;
    }
    ans[0] = totalSum;
    ans[1] = count;
}
 
// Counts number of inversions when merging
long merge_inversion_count(int arr[], int left[],
                           int right[], int nL, int nR)
{
    int i = 0, j = 0, count = 0;
    while (i < nL || j < nR) {
        if (i == nL) {
            arr[i + j] = right[j];
            j++;
        }
        else if (j == nR) {
            arr[i + j] = left[i];
            i++;
        }
        else if (left[i] <= right[j]) {
            arr[i + j] = left[i];
            i++;
        }
        else {
            arr[i + j] = right[j];
            count += nL - i;
            j++;
        }
    }
    return count;
}
 
// Divide and conquer approach -- splits array and
// counts
// inversions via merge method
long subarray_inversion_count_initial(int arr[], int n)
{
    if (n < 2)
        return 0;
 
    int m = (n + 1) / 2;
    int left[m];
    copy(arr, arr + m, left);
    int right[n - m];
    copy(arr + m, arr + n, right);
 
    return subarray_inversion_count_initial(left, m)
           + subarray_inversion_count_initial(right, n - m)
           + merge_inversion_count(arr, left, right, m,
                                   n - m);
}
 
// Driver code
int main()
{
 
    // Code
    int n = 10;
    int arr[] = { 15, 51, 44, 44, 76, 50, 29, 88, 48, 50 };
    int k = 5;
 
    long result = inversion_count(n, k, arr);
    cout << result;
    return 0;
}
 
// This code is contributed by akashish__


Java




import java.util.*;
 
public class Subarray_Inversions {
 
    // Inversion counting method, slides window of [start,
    // end] across array
    static long inversion_count(int n, int m, int[] arr)
    {
        long count = 0;
        count += subarray_inversion_count_initial(Arrays.copyOfRange(arr, 0, m));
        long subarray_count = subarray_inversion_count_initial(Arrays.copyOfRange(arr, 1, m));
        for (int start = 1; start <= n - m; start++) {
            int end = start + m - 1;
            long[] ans = subarray_inversion_count(arr, start, end, subarray_count);
            count += ans[0];
            subarray_count = ans[1];
        }
        return count;
    }
 
    // start >=1; find inversion in interval [start, end)
    public static long[] subarray_inversion_count(int[] arr, int start,
                                          int end, long subarray_count)
    {
        int new_element = arr[end];
        long count = subarray_count;
        for (int i = start; i < end; i++) {
            if (new_element < arr[i])
                count++;
        }
        long totalSum = count;
        int last_element = arr[start];
        for (int i = start + 1; i <= end; i++) {
            if (last_element > arr[i])
                count--;
        }
        long[] ans = { totalSum, count };
        return ans;
    }
 
    // Counts number of inversions when merging
    public static long merge_inversion_count(int[] arr, int[] left,
                                                       int[] right)
    {
        int i = 0, j = 0, count = 0;
        while (i < left.length || j < right.length) {
            if (i == left.length) {
                arr[i + j] = right[j];
                j++;
            } else if (j == right.length) {
                arr[i + j] = left[i];
                i++;
            } else if (left[i] <= right[j]) {
                arr[i + j] = left[i];
                i++;
            } else {
                arr[i + j] = right[j];
                count += left.length - i;
                j++;
            }
        }
        return count;
    }
 
    // Divide and conquer approach -- splits array and counts
    // inversions via merge method
    public static long subarray_inversion_count_initial(int[] arr)
    {
        if (arr.length < 2)
            return 0;
 
        int m = (arr.length + 1) / 2;
        int left[] = Arrays.copyOfRange(arr, 0, m);
        int right[] = Arrays.copyOfRange(arr, m, arr.length);
 
        return subarray_inversion_count_initial(left) +
               subarray_inversion_count_initial(right) +
               merge_inversion_count(arr, left, right);
    }
 
    public static void main(String[] args) throws Exception
    {
        int n = 10;
        int[] arr = { 15, 51, 44, 44, 76, 50, 29, 88, 48, 50 };
        int k = 5;
 
        long result = inversion_count(n, k, arr);
        System.out.println(result);
    }
}


Python3





C#




// C# code for the above approach
 
using System;
using System.Collections.Generic;
 
public class GFG {
 
    // Inversion counting method, slides window of [start,
    // end] across array
    static long inversion_count(int n, int m, int[] arr)
    {
        long count = 0;
        int[] array1 = new int[m];
        Array.Copy(arr, 0, array1, 0, m);
        count += subarray_inversion_count_initial(array1);
        int[] array2 = new int[m - 1];
        Array.Copy(arr, 1, array2, 0, m - 1);
        long subarray_count
            = subarray_inversion_count_initial(array2);
        for (int start = 1; start <= n - m; start++) {
            int end = start + m - 1;
            long[] ans = subarray_inversion_count(
                arr, start, end, subarray_count);
            count += ans[0];
            subarray_count = ans[1];
        }
        return count;
    }
 
    // start >=1; find inversion in interval [start, end)
    public static long[] subarray_inversion_count(
        int[] arr, int start, int end, long subarray_count)
    {
        int new_element = arr[end];
        long count = subarray_count;
        for (int i = start; i < end; i++) {
            if (new_element < arr[i])
                count++;
        }
        long totalSum = count;
        int last_element = arr[start];
        for (int i = start + 1; i <= end; i++) {
            if (last_element > arr[i])
                count--;
        }
        long[] ans = { totalSum, count };
        return ans;
    }
 
    // Counts number of inversions when merging
    public static long merge_inversion_count(int[] arr,
                                             int[] left,
                                             int[] right)
    {
        int i = 0, j = 0, count = 0;
        while (i < left.Length || j < right.Length) {
            if (i == left.Length) {
                arr[i + j] = right[j];
                j++;
            }
            else if (j == right.Length) {
                arr[i + j] = left[i];
                i++;
            }
            else if (left[i] <= right[j]) {
                arr[i + j] = left[i];
                i++;
            }
            else {
                arr[i + j] = right[j];
                count += left.Length - i;
                j++;
            }
        }
        return count;
    }
 
    // Divide and conquer approach -- splits array and
    // counts
    // inversions via merge method
    public static long
    subarray_inversion_count_initial(int[] arr)
    {
        if (arr.Length < 2)
            return 0;
 
        int m = (arr.Length + 1) / 2;
        int[] left = new int[m];
        Array.Copy(arr, 0, left, 0, m);
        int[] right = new int[arr.Length - m];
        Array.Copy(arr, m, right, 0, arr.Length - m);
 
        return subarray_inversion_count_initial(left)
            + subarray_inversion_count_initial(right)
            + merge_inversion_count(arr, left, right);
    }
 
    static public void Main()
    {
 
        // Code
        int n = 10;
        int[] arr
            = { 15, 51, 44, 44, 76, 50, 29, 88, 48, 50 };
        int k = 5;
 
        long result = inversion_count(n, k, arr);
        Console.WriteLine(result);
    }
}
 
// This code is contributed by lokeshmvs21.


Javascript




//JavaScript code for the above approach
 
// Inversion counting method, slides window of
// [start, end] across array
function inversion_count(n, m, arr) {
    let count = 0;
    count += subarray_inversion_count_initial(arr.slice(0, m));
    let subarray_count = subarray_inversion_count_initial(arr.slice(1, m));
    for (let start = 1; start < n - m + 1; start++) {
        let end = start + m - 1;
        let ans = subarray_inversion_count(arr, start, end, subarray_count);
        count += ans[0];
        subarray_count = ans[1];
    }
    return count;
}
 
// start >=1; find inversion in interval [start, end)
function subarray_inversion_count(arr, start, end, subarray_count) {
    let new_element = arr[end];
    let count = subarray_count;
    for (let i = start; i < end; i++) {
        if (new_element < arr[i]) {
            count += 1;
        }
    }
    let totalSum = count;
    let last_element = arr[start];
    for (let i = start + 1; i <= end; i++) {
        if (last_element > arr[i]) {
            count -= 1;
        }
    }
    let ans = [totalSum, count];
    return ans;
}
 
// Counts number of inversions when merging
function merge_inversion_count(arr, left, right) {
    let i = 0;
    let j = 0;
    let count = 0;
    while (i < left.length || j < right.length) {
        if (i == left.length) {
            arr[i + j] = right[j];
            j += 1;
        } else if (j == right.length) {
            arr[i + j] = left[i];
            i += 1;
        } else if (left[i] <= right[j]) {
            arr[i + j] = left[i];
            i += 1;
        } else {
            arr[i + j] = right[j];
            count += left.length - i;
            j += 1;
        }
    }
    return count;
}
 
// Divide and conquer approach -- splits array and counts
// inversions via merge method
function subarray_inversion_count_initial(arr) {
    if (arr.length < 2) {
        return 0;
    }
    let m = Math.floor((arr.length + 1) / 2);
    let left = arr.slice(0, m);
    let right = arr.slice(m);
    return subarray_inversion_count_initial(left) + subarray_inversion_count_initial(right) + merge_inversion_count(arr, left, right);
}
 
let n = 10;
let arr = [15, 51, 44, 44, 76, 50, 29, 88, 48, 50];
let k = 5;
 
let result = inversion_count(n, k, arr);
console.log(result);
// contributed by akashish__


Output

25

[4] Binary Indexed Tree Implementation: Iterating over each window seems inevitable, so the bottleneck here appears to be the way we handle the windows. We know that consecutive windows overlap significantly, so all we need to know is the number of elements greater than the newly added element and number of elements less than the newly removed element. Many times, algorithms that perform the same or similar operation(s) repeatedly can be improved by the use of a more robust data structure. In our case, we’re looking for a dynamic data structure that can efficiently answer queries about an element’s relative position when sorted. We can use a self-balancing binary tree to actually maintain a sorted list, but insertion/removal takes logarithmic time. We can do this in constant time using a Binary Indexed Tree, or Fenwick Tree. 

A binary indexed tree is a tree represented in the form of an array. It uses clever bit manipulation to compute the cumulative sum of elements very efficiently. We can call the function update(index, val) function to add val to BIT[index] and all of the ancestors of index. The function read(index) returns the sum of the values stored at BIT[index] and all of the ancestors of index in the tree. Thus, calling read(index) returns the cumulative sum of all elements in BIT less than or equal to index. Instead of storing values, if we simply store 1, we can use read(index + 1) to determine the number of elements less than index. Now, we can construct a binary indexed tree by inserting the elements (updating) of the first window. For subsequent windows, we can remove the trailing element by calling update(tail_element, -1) and add the new element with update(head_element, 1). Since this is a tree, the longest possible root-node path is O(logk), Thus, we achieve an optimal runtime of O(nlogk + klogk) = O(nlogk)!

Or do we…? Remember, binary indexed trees allocate memory for every possible value in the range [0, max_element], so this requires O(max_element) time and space. For very sparse arrays, this can be quite costly. Instead, we can define a hash function to . We can do this because we’re only concerned about inversions — as long as we keep the relative magnitude the same (i.e. A[i] <= A ==> A[hash(i)] <= A[hash(j)]), our solution will still be correct. Thus, we can map all the elements in A to the set {0, 1, 2, …, n}, yielding a guaranteed runtime of O(nlogk).

Implementation:

C++




// C++ program for the above approach
#include <bits/stdc++.h>
using namespace std;
 
// Implementation of Binary Indexed Tree
class BIT {
  vector<int> tree;
  int maxVal;
 
  public:
  BIT(int N) {
    tree.assign(N + 1, 0);
    maxVal = N;
  }
 
  // Updates BIT, starting with element at index
  // and all ancestors
  void update(int index, int val) {
    while (index <= maxVal) {
      tree[index] += val;
      index += (index & -index);
    }
  }
 
  // Returns the cumulative frequency of index
  // starting with element at index and all ancestors
  int read(int index) {
    index -= 1;
    int cumulative_sum = 0;
    while (index > 0) {
      cumulative_sum += tree[index];
      index -= (index & -index);
    }
    return cumulative_sum;
  }
};
 
// first window, counts first k elements and creates
// BIT
int inversionCountBIT1(vector<int>& arr, int start, int end, BIT& bit) {
  int count = 0;
  for (int index = start; index >= end; index--) {
    count += bit.read(arr[index]);
    bit.update(arr[index], 1);
  }
  return count;
}
 
// subsequent windows, removes previous element, adds
// new element, and increments change in inversion
int inversionCountBIT2(vector<int>& arr, int start, int end, int val, BIT& bit) {
  bit.update(arr[start + 1], -1);
 
  // find number of elements in range [start, end-1]
  // greater than first
  int numGreaterThanFirst = start - end - bit.read(arr[start + 1] + 1);
  int count = val + bit.read(arr[end]) - numGreaterThanFirst;
  bit.update(arr[end], 1);
  return count;
}
 
// Main method to count inversions in size k subarrays
int inversionCount(int n, int k, vector<int>& arr) {
  BIT bit(n);
  map<int, int> freq;
  vector<int> asort = arr;
 
  // Map elements from [A[0]...A[n-1]] to [1...n]
  sort(asort.begin(), asort.end());
  int current = 1;
  for (int i = 0; i < n; i++) {
    if (freq.find(asort[i]) == freq.end()) {
      freq[asort[i]] = current;
      current += 1;
    }
  }
 
  for (int i = 0; i < n; i++) {
    arr[i] = freq[arr[i]];
  }
 
  int count = 0, val = 0;
 
  // [start - end] ==> start - end = k+1
  for (int start = n - 1; start >= k - 1; start--) {
    int end = start - k + 1;
    if (start == n - 1) {
      val = inversionCountBIT1(arr, n - 1, n - k, bit);
    }
    else { // subsequent passes
      val = inversionCountBIT2(arr, start, end, val, bit);
    }
    count += val;
  }
  return count;
}
 
int main() {
  int n = 10;
  vector<int> arr = {15, 51, 44, 44, 76, 50, 29, 88, 48, 50};
  int k = 5;
  int result = inversionCount(n, k, arr);
  cout << result << endl;
  return 0;
}
 
// This code is contributed by Prince Kumar


Java




import java.util.*;
 
public class Subarray_Inversions {
 
    // Declare binary indexed tree with global scope
    static BIT bit;
 
    // first window, counts first k elements and creates
    // BIT
    static long inversionCountBIT1(int[] arr, int start,
                                               int end)
    {
        bit = new BIT(arr.length);
        long count = 0;
        for (int index = start; index >= end; index--) {
            count += bit.read(arr[index]);
            bit.update(arr[index], 1);
        }
        return count;
    }
 
    // subsequent windows, removes previous element, adds
    // new element, and increments change in inversions
    static long inversionCountBIT2(int[] arr, int start,
                                       int end, long val)
    {
        bit.update(arr[start + 1], -1); // remove trailing element
 
        // find number of elements in range [start, end-1]
        // greater than first
        int numGreaterThanFirst = start - end - bit.read(arr[start + 1] + 1);
        long count = val + bit.read(arr[end]) - numGreaterThanFirst;
        bit.update(arr[end], 1); // adds leading element
 
        return count;
    }
 
    // Main method to count inversions in size k subarrays
    public static long inversionCount(int n, int k, int[] arr)
    {
        bit = new BIT(n);
        HashMap<Integer, Integer> freq = new HashMap<Integer, Integer>();
        int[] asort = arr.clone();
 
        // Map elements from [A[0]...A[n-1]] to [1...n]
        Arrays.sort(asort);
        int index = 0;
        int current = 1;
        for (int i = 0; i < n; i++) {
            if (!freq.containsKey(asort[i])) {
                freq.put(asort[i], current);
                current++;
            }
        }
        for (int i = 0; i < n; i++) {
            arr[i] = freq.get(arr[i]);
        }
 
        long count = 0;
        long val = 0;
 
        //[start - end] ==> start - end = k+1
        for (int start = n - 1; start >= k - 1; start--) {
            int end = start - k + 1;
            if (start == n - 1) { // First pass
                val = inversionCountBIT1(arr, n - 1, n - k);
            } else { // subsequent passes
                val = inversionCountBIT2(arr, start, end, val);
            }
            count += val;
        }
        return count;
    }
 
    public static void main(String[] args) throws Exception
    {
        int n = 10;
        int[] arr = { 15, 51, 44, 44, 76, 50, 29, 88, 48, 50 };
        int k = 5;
 
        long result = inversionCount(n, k, arr);
        System.out.println(result);
    }
 
    // Implementation of Binary Indexed Tree
    static class BIT {
        int[] tree;
        int maxVal;
    public BIT(int N)
        {
            tree = new int[N + 1];
            maxVal = N;
        }
 
        // Updates BIT, starting with element at index
        // and all ancestors
        void update(int index, int val)
        {
            while (index <= maxVal) {
                tree[index] += val;
                index += (index & -index);
            }
        }
 
        // Returns the cumulative frequency of index
        // starting with element at index and all ancestors
        int read(int index)
        {
            --index;
            int cumulative_sum = 0;
            while (index > 0) {
                cumulative_sum += tree[index];
                index -= (index & -index);
            }
            return cumulative_sum;
        }
    };
}


Python3




# Implementation of Binary Indexed Tree
class BIT:
    def __init__(self, N):
        self.tree = [0] * (N + 1)
        self.maxVal = N
 
      # Updates BIT, starting with element at index
        # and all ancestors
    def update(self, index, val):
        while index <= self.maxVal:
            self.tree[index] += val
            index += (index & -index)
 
  # Returns the cumulative frequency of index
    # starting with element at index and all ancestors
    def read(self, index):
        index -= 1
        cumulative_sum = 0
        while index > 0:
            cumulative_sum += self.tree[index]
            index -= (index & -index)
        return cumulative_sum
 
   # first window, counts first k elements and creates
    # BIT
 
 
def inversionCountBIT1(arr, start, end, bit):
    count = 0
    for index in range(start, end - 1, -1):
        count += bit.read(arr[index])
        bit.update(arr[index], 1)
    return count
 
    # subsequent windows, removes previous element, adds
    # new element, and increments change in inversi
 
 
def inversionCountBIT2(arr, start, end, val, bit):
    bit.update(arr[start + 1], -1)
 
    # find number of elements in range [start, end-1]
    # greater than first
    numGreaterThanFirst = start - end - bit.read(arr[start + 1] + 1)
    count = val + bit.read(arr[end]) - numGreaterThanFirst
    bit.update(arr[end], 1)
    return count
 
# Main method to count inversions in size k subarrays
 
 
def inversionCount(n, k, arr):
    bit = BIT(n)
    freq = {}
    asort = arr.copy()
 
    # Map elements from [A[0]...A[n-1]] to [1...n]
    asort.sort()
    current = 1
    for i in range(n):
        if asort[i] not in freq:
            freq[asort[i]] = current
            current += 1
 
    for i in range(n):
        arr[i] = freq[arr[i]]
 
    count = 0
    val = 0
 
  # [start - end] ==> start - end = k+1
    for start in range(n - 1, k - 2, -1):
        end = start - k + 1
        if start == n - 1:
            val = inversionCountBIT1(arr, n - 1, n - k, bit)
        else# subsequent passes
            val = inversionCountBIT2(arr, start, end, val, bit)
        count += val
    return count
 
 
def main():
    n = 10
    arr = [15, 51, 44, 44, 76, 50, 29, 88, 48, 50]
    k = 5
    result = inversionCount(n, k, arr)
    print(result)
 
 
if __name__ == '__main__':
    main()


C#




using System;
using System.Collections.Generic;
 
class Subarray_Inversions{
     
// Implementation of Binary Indexed Tree
public class BIT
{
    public int[] tree;
    public int maxVal;
     
    public BIT(int N)
    {
        tree = new int[N + 1];
        maxVal = N;
    }
 
    // Updates BIT, starting with element
    // at index and all ancestors
    public void update(int index, int val)
    {
        while (index <= maxVal)
        {
            tree[index] += val;
            index += (index & -index);
        }
    }
 
    // Returns the cumulative frequency
    // of index starting with element at
    // index and all ancestors
    public int read(int index)
    {
        --index;
        int cumulative_sum = 0;
         
        while (index > 0)
        {
            cumulative_sum += tree[index];
            index -= (index & -index);
        }
        return cumulative_sum;
    }
};
 
// Declare binary indexed tree
// with global scope
static BIT bit;
 
// First window, counts first k elements
// and creates BIT
static long inversionCountBIT1(int[] arr, int start,
                               int end)
{
    bit = new BIT(arr.Length);
    long count = 0;
     
    for(int index = start; index >= end; index--)
    {
        count += bit.read(arr[index]);
        bit.update(arr[index], 1);
    }
    return count;
}
 
// Subsequent windows, removes previous element, adds
// new element, and increments change in inversions
static long inversionCountBIT2(int[] arr, int start,
                               int end, long val)
{
     
    // Remove trailing element
    bit.update(arr[start + 1], -1);
 
    // Find number of elements in
    // range [start, end-1] greater
    // than first
    int numGreaterThanFirst = start - end -
                 bit.read(arr[start + 1] + 1);
    long count = val + bit.read(arr[end]) -
                 numGreaterThanFirst;
                  
    bit.update(arr[end], 1); // Adds leading element
 
    return count;
}
 
// Main method to count inversions in size k subarrays
public static long inversionCount(int n, int k, int[] arr)
{
    bit = new BIT(n);
    Dictionary<int,
               int> freq = new Dictionary<int,
                                          int>();
                                           
    int[] asort = (int[])arr.Clone();
 
    // Map elements from [A[0]...A[n-1]] to [1...n]
    Array.Sort(asort);
    //int index = 0;
    int current = 1;
     
    for(int i = 0; i < n; i++)
    {
        if (!freq.ContainsKey(asort[i]))
        {
            freq.Add(asort[i], current);
            current++;
        }
    }
    for(int i = 0; i < n; i++)
    {
        arr[i] = freq[arr[i]];
    }
 
    long count = 0;
    long val = 0;
 
    //[start - end] ==> start - end = k+1
    for(int start = n - 1; start >= k - 1; start--)
    {
        int end = start - k + 1;
         
        if (start == n - 1)// First pass
        {
            val = inversionCountBIT1(arr, n - 1,
                                          n - k);
        }
        else // subsequent passes
        {
            val = inversionCountBIT2(arr, start,
                                     end, val);
        }
        count += val;
    }
    return count;
}
 
// Driver code
public static void Main(String[] args)
{
    int n = 10;
    int[] arr = { 15, 51, 44, 44, 76,
                  50, 29, 88, 48, 50 };
    int k = 5;
    long result = inversionCount(n, k, arr);
     
    Console.WriteLine(result);
}
}
 
// This code is contributed by Amit Katiyar


Javascript




// Javascript program for the above approach
 
// Implementation of Binary Indexed Tree
class BIT {
  constructor(N) {
    this.tree = Array(N + 1).fill(0);
    this.maxVal = N;
  }
 
  // Updates BIT, starting with element at index
  // and all ancestors
  update(index, val) {
    while (index <= this.maxVal) {
      this.tree[index] += val;
      index += (index & -index);
    }
  }
 
  // Returns the cumulative frequency of index
  // starting with element at index and all ancestors
  read(index) {
    index -= 1;
    let cumulative_sum = 0;
    while (index > 0) {
      cumulative_sum += this.tree[index];
      index -= (index & -index);
    }
    return cumulative_sum;
  }
}
 
// first window, counts first k elements and creates
// BIT
function inversionCountBIT1(arr, start, end, bit) {
  let count = 0;
  for (let index = start; index > end - 1; index--) {
    count += bit.read(arr[index]);
    bit.update(arr[index], 1);
  }
  return count;
}
 
// subsequent windows, removes previous element, adds
// new element, and increments change in inversion
function inversionCountBIT2(arr, start, end, val, bit) {
  bit.update(arr[start + 1], -1);
 
  // find number of elements in range [start, end-1]
  // greater than first
  const numGreaterThanFirst = start - end - bit.read(arr[start + 1] + 1);
  const count = val + bit.read(arr[end]) - numGreaterThanFirst;
  bit.update(arr[end], 1);
  return count;
}
 
// Main method to count inversions in size k subarrays
function inversionCount(n, k, arr) {
  const bit = new BIT(n);
  const freq = {};
  const asort = [...arr];
 
  // Map elements from [A[0]...A[n-1]] to [1...n]
  asort.sort((a, b) => a - b);
  let current = 1;
  for (let i = 0; i < n; i++) {
    if (!(asort[i] in freq)) {
      freq[asort[i]] = current;
      current += 1;
    }
  }
 
  for (let i = 0; i < n; i++) {
    arr[i] = freq[arr[i]];
  }
 
  let count = 0;
  let val = 0;
 
  // [start - end] ==> start - end = k+1
  for (let start = n - 1; start > k - 2; start--) {
    const end = start - k + 1;
    if (start === n - 1) {
      val = inversionCountBIT1(arr, n - 1, n - k, bit);
    } else { // subsequent passes
      val = inversionCountBIT2(arr, start, end, val, bit);
    }
    count += val;
  }
  return count;
}
 
// Testing the inversionCount function
const n = 10;
const arr = [15, 51, 44, 44, 76, 50, 29, 88, 48, 50];
const k = 5;
const result = inversionCount(n, k, arr);
console.log(result);
 
 
// This code is contributed by princekumaras


Output

25

 



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads