worker_bee
worker_bee

Reputation: 455

Issue with understanding fencepost handling with merge-sort

Fencepost (AKA An off-by-one error (OBOE), also commonly known as an OBOB (off-by-one bug).

Given an array, I would generally iterate through index 0 to array.length() (Half-open interval).

I've noticed that certain versions of merge-sort require the mid value to be (start+end)/2. And when you calculate the number of elements in the merge process then we sometimes refer to using (end - start) as number of elements or (end - mid + 1). I am not able to intuitively get this? I somehow have difficulty understanding this and feel like am mugging up each time I look at any new implementation.

Can someone provide an intuitive way to understanding how I can apply/recognize a fencepost problem? Is this the same for multi-dimensional arrays?

public static BigInteger mergeSort(int[] integerArray, int start, int end) {
    if (start >= end) { // less than equal to is important for cases when start = end = 0
        return BigInteger.valueOf(0);
    }
    int middle = (start + end) / 2;
    BigInteger numInv1 = mergeSort(integerArray, start, middle);
    BigInteger numInv2 = mergeSort(integerArray, middle + 1, end);
    BigInteger numInv3 = runMerge(integerArray, start, middle, end);
    return numInv1.add(numInv2).add(numInv3);
}

private static BigInteger runMerge(int[] integerArray,
                                   int start, int middle, int end) {
    BigInteger numInv = BigInteger.valueOf(0);
    int n1 = middle - start + 1;
    /*
    number of elements in 1st array is middle - start + 1. why ?
    */

    int n2 = end - middle;       // number of elements in 2nd array
    /*
    number of elements in 2nd array is end - middle. why ?
    */

    int []p = new int[n1];
    int []q = new int[n2];
    int i, j, k;
    for (i = 0; i < n1 ; i++) {
        p[i] = integerArray[start + i];
    }
    for (j = 0; j < n2; j++) {
        q[j] = integerArray[middle + j + 1];
        //Why do we do +1 here? because we use 2nd array for mid+1 till end elements
    }
    i = 0;
    j = 0;
    k = start;
    while ( i < n1 && j < n2) {
        if (p[i] <= q[j]) {
            integerArray[k++] = p[i++];
        } else {
            integerArray[k++] = q[j++];
            numInv = numInv.add(BigInteger.valueOf(n1-i));
        }
    }
    while ( i < n1 ) {
        integerArray[k++] = p[i++];
    }
    while ( j < n2 ) {
        integerArray[k++] = q[j++];
    }
    return numInv;
}

Upvotes: 1

Views: 95

Answers (1)

curveball
curveball

Reputation: 4505

number of elements in 1st array is middle - start + 1. why? number of elements in 2nd array is end - middle. why?

They are not numbers of elements, they are elements' edge indexes needed to break initial array into smaller arrays. Suppose you have an array to sort:

int[] myIntArray = {7,4,3,5,1,12,12,11,0,2};

It contains 10 elements but the indexes go from 0 to 9. So, in your method mergeSort(int[] integerArray, int start, int end); it should be myIntArray, 0, 9, not myIntArray, 1, 10 nor myIntArray, 1, 9.

So, assuming we pass arguments like myIntArray, 0, 9, lets look into the last (folding direction) call of mergeSort() when we have two sorted subarrays:

After calculating middle = (0 + 9) / 2 = 4, we mentally broke our initial array into 2 arrays like this:

mergeSort(integerArray, start, middle); where start = 0 and middle = 4 (comprises the items with indexes from 0 to 4 - both included: 1,3,4,5,7)

and

mergeSort(integerArray, middle + 1, end); where start = middle + 1 = 4 + 1 = 5 and end = 9 (comprises the numbers with indexes from 5 to 9 - both included: 0,2,11,12,12).

Here

q[j] = integerArray[middle + j + 1];

by adding +1 we are getting to the 5th element. Remember that in the stack for the current call variable middle is equal to 4 and this value (4) was passed to runMerge(). Value middle + 1 went to one of the previously finished calls of mergeSort().

The whole proceeding goes on untill we get arrays of size 1 that can be considered sorted and then we start to merge - sure, you already know this. As you can see, those variables - start, middle, end - are the positions (indexes) of elements and not numbers of elements.

The code you posted works fine if you treat arguments as items' positions like mergeSort(myIntArray, 0, 9); and not as number of items. In the beginning it should be array.length() - 1 Hope it helps :)

Upvotes: 0

Related Questions