Subarray Inversions
Last Updated :
22 Mar, 2023
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++
#include <iostream>
using namespace std;
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;
}
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;
}
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;
}
|
Java
public class Subarray_Inversions {
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;
}
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
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;
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;
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);
|
C#
using System;
public class Subarray_Inversions
{
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;
}
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()
{
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);
}
}
|
Javascript
<script>
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;
}
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);
</script>
|
[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;
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;
}
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);
}
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);
}
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;
}
|
Java
import java.util.*;
public class Subarray_Inversions {
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;
}
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;
}
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 :
@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
@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
@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([])
|
C#
using System;
using System.Collections.Generic;
public class Subarray_Inversions {
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;
}
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;
}
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);
}
}
|
Javascript
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;
}
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);
}
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);
}
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);
|
[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++
#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);
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;
}
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;
}
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;
}
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);
}
int 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);
cout << result;
return 0;
}
|
Java
import java.util.*;
public class Subarray_Inversions {
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;
}
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;
}
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;
}
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#
using System;
using System.Collections.Generic;
public class GFG {
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;
}
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;
}
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;
}
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()
{
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);
}
}
|
Javascript
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;
}
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;
}
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;
}
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);
|
[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++
#include <bits/stdc++.h>
using namespace std;
class BIT {
vector< int > tree;
int maxVal;
public :
BIT( int N) {
tree.assign(N + 1, 0);
maxVal = N;
}
void update( int index, int val) {
while (index <= maxVal) {
tree[index] += val;
index += (index & -index);
}
}
int read( int index) {
index -= 1;
int cumulative_sum = 0;
while (index > 0) {
cumulative_sum += tree[index];
index -= (index & -index);
}
return cumulative_sum;
}
};
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;
}
int inversionCountBIT2(vector< int >& arr, int start, int end, int val, BIT& bit) {
bit.update(arr[start + 1], -1);
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;
}
int inversionCount( int n, int k, vector< int >& arr) {
BIT bit(n);
map< int , int > freq;
vector< int > asort = arr;
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;
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 {
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;
}
|
Java
import java.util.*;
public class Subarray_Inversions {
static BIT 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;
}
static long inversionCountBIT2( int [] arr, int start,
int end, long val)
{
bit.update(arr[start + 1 ], - 1 );
int numGreaterThanFirst = start - end - bit.read(arr[start + 1 ] + 1 );
long count = val + bit.read(arr[end]) - numGreaterThanFirst;
bit.update(arr[end], 1 );
return count;
}
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();
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 ;
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);
} else {
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);
}
static class BIT {
int [] tree;
int maxVal;
public BIT( int N)
{
tree = new int [N + 1 ];
maxVal = N;
}
void update( int index, int val)
{
while (index <= maxVal) {
tree[index] += val;
index += (index & -index);
}
}
int read( int index)
{
--index;
int cumulative_sum = 0 ;
while (index > 0 ) {
cumulative_sum += tree[index];
index -= (index & -index);
}
return cumulative_sum;
}
};
}
|
Python3
class BIT:
def __init__( self , N):
self .tree = [ 0 ] * (N + 1 )
self .maxVal = N
def update( self , index, val):
while index < = self .maxVal:
self .tree[index] + = val
index + = (index & - index)
def read( self , index):
index - = 1
cumulative_sum = 0
while index > 0 :
cumulative_sum + = self .tree[index]
index - = (index & - index)
return cumulative_sum
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
def inversionCountBIT2(arr, start, end, val, bit):
bit.update(arr[start + 1 ], - 1 )
numGreaterThanFirst = start - end - bit.read(arr[start + 1 ] + 1 )
count = val + bit.read(arr[end]) - numGreaterThanFirst
bit.update(arr[end], 1 )
return count
def inversionCount(n, k, arr):
bit = BIT(n)
freq = {}
asort = arr.copy()
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
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 :
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{
public class BIT
{
public int [] tree;
public int maxVal;
public BIT( int N)
{
tree = new int [N + 1];
maxVal = N;
}
public void update( int index, int val)
{
while (index <= maxVal)
{
tree[index] += val;
index += (index & -index);
}
}
public int read( int index)
{
--index;
int cumulative_sum = 0;
while (index > 0)
{
cumulative_sum += tree[index];
index -= (index & -index);
}
return cumulative_sum;
}
};
static BIT 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;
}
static long inversionCountBIT2( int [] arr, int start,
int end, long val)
{
bit.update(arr[start + 1], -1);
int numGreaterThanFirst = start - end -
bit.read(arr[start + 1] + 1);
long count = val + bit.read(arr[end]) -
numGreaterThanFirst;
bit.update(arr[end], 1);
return count;
}
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();
Array.Sort(asort);
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;
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);
}
else
{
val = inversionCountBIT2(arr, start,
end, val);
}
count += val;
}
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 = inversionCount(n, k, arr);
Console.WriteLine(result);
}
}
|
Javascript
class BIT {
constructor(N) {
this .tree = Array(N + 1).fill(0);
this .maxVal = N;
}
update(index, val) {
while (index <= this .maxVal) {
this .tree[index] += val;
index += (index & -index);
}
}
read(index) {
index -= 1;
let cumulative_sum = 0;
while (index > 0) {
cumulative_sum += this .tree[index];
index -= (index & -index);
}
return cumulative_sum;
}
}
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;
}
function inversionCountBIT2(arr, start, end, val, bit) {
bit.update(arr[start + 1], -1);
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;
}
function inversionCount(n, k, arr) {
const bit = new BIT(n);
const freq = {};
const asort = [...arr];
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;
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 {
val = inversionCountBIT2(arr, start, end, val, bit);
}
count += val;
}
return count;
}
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);
|
Share your thoughts in the comments
Please Login to comment...