I'm trying to write code to find inversions in an array of numbers. I've seen the algorithm that uses Merge Sort to correctly do so, but have a question regarding the logic.
Why do we have to count the inversions in the left and right subarray and those between the two? I mean obviously that is correct, but why can't we just count all the inversions in the merge step? It turns out that at every level of doing merge the number of inversions can be counted directly, and after the subarray of twice the size is produced, the relative order of any two elements of it will never change in subsequent merge steps, which means we will never count any inversion that will not be an inversion in the end. So, what's wrong with the logic, cause it doesn't give the right answer.
I can explain more with examples and/or code if you want; intentionally didn't include the code so no one suspects this is homework.
Edit: So far I've found nothing wrong with the logic, but instead a bug in the code that leads to the wrong answer.
So, here's the method to find inversions in the array:
private void findInversions(int begin, int end, int count) {
if (end - begin < 1)
return;
int middle = (begin + end) / 2;
findInversions(begin, middle, count);
System.out.println("But here count is " + count + " and I can't find why.");
findInversions(middle + 1, end, count);
count = mergeAndCount(begin, middle, end, count);
System.out.println("count now is: " + count);
}
The mergeAndCount()
method works the obvious way:
private int mergeAndCount(int begin, int middle, int end, int count) {
int[] result = new int[end - begin + 1];
int aptr = begin;
int bptr = middle + 1;
for (int i = 0; i < result.length; i++) {
if (aptr <= middle && bptr <= end) {
if (numbers[aptr] < numbers[bptr]) {
result[i] = numbers[aptr];
aptr++;
}
else {
// (a[aptr], b[bptr]) is an inversion here
count++;
System.out.println("Found: (" + numbers[aptr] + "," + numbers[bptr] + "). If you print 'count' here it'll be one. See: " + count);
result[i] = numbers[bptr];
bptr++;
}
}
else if (aptr > middle) {
result[i] = numbers[bptr];
bptr++;
}
else if (bptr > end) {
result[i] = numbers[aptr];
aptr++;
}
}
for (int i = 0; i < result.length; i++) {
numbers[begin + i] = result[i];
}
return count;
}
And I call the method with count set to zero: findInversions(0, numbers.length - 1, 0)
.
Obviously the problem is that count
doesn't keep its value across different recursions. This is Java, so I don't need to pass it with the ampersand, and I even return its value in mergeAndCount()
. So what's wrong here?