Search code examples
c++recursionmergesortdivide-and-conquer

Counting inversion using merge sort


I know that there was a plenty of these implementation here in Stack, but I have got a problem which I cannot handle.

First I have implemented the merge sort at khanacademy with javascript, then I rewrite the code to C++, and tried to count number of inversion in an array.

I did the best I could, and spent an hour trying to get understand what have I done wrong. I did search for another implementation here at stack and tried to correct my code. Unfortunately I do not know what am I doing wrong.I think that I count every inversion. Thanks in advance for an assistance with having understood what is wrong.

My code:

int lowhalflength(int p, int q)
{
    return q - p + 1;
}

int highhalflength(int q, int r)
{
    return r - q;
}


int merge(int array[], int p, int q, int r, int lowhalf[], int highhalf[])
{
    int k = p;
    int i;
    int j;
    int count = 0;
    for (int i = 0; k <= q; i++ , k++)
    {
        lowhalf[i] = array[k];
    }
    for (int i = 0; k <= r; i++ , k++)
    {
        highhalf[i] = array[k];
    }

    k = p;
    i = 0;
    j = 0;

    while (i <= (q - p) && j <= r - (q + 1))
    {
        if (lowhalf[i] <= highhalf[j])
        {
            array[k] = lowhalf[i];
            i++;
        }
        else
        {
            array[k] = highhalf[j];
            j++;
            count += q - 1;
        }

        k++;
    }

    while (i < lowhalflength(p, q))
    {
        array[k] = lowhalf[i];
        k++;
        i++;
    }

    while (j < highhalflength(q, r))
    {
        array[k] = highhalf[j];
        k++;
        j++;
    }


    return count;
}

The mergeSort function:

int mergeSort(int array[], int p, int r)
{
    int q = ((p + r) / 2);
    int* lowhalf = new int[lowhalflength(p, q)];
    int* highhalf = new int[highhalflength(q, r)];

    int count = 0;
    if (p < r)
    {
        q = ((p + r) / 2);
        count = mergeSort(array, p, q);
        count += mergeSort(array, q + 1, r);
        count += merge(array, p, q, r, lowhalf, highhalf);
    }
    delete[] lowhalf;
    delete[] highhalf;
    return count;
}

For array [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] the output is 46 while it should be 45.

EDIT: The answer is to change the following line q-1 to q+j-k. I found it by myself but I do not know how should I interpret it. Any hint or proof why it is correct will be really desirable.


Solution

  • You can use my code for counting inversion pair and your merge function should look like this in more efficient manner as:

    int merge(int *array, int lower, int mid, int upper) {
    
        // Initialisation of the sizes of two subarrays and subarrays also.
        int left_array_size = mid - lower + 1;
        int right_array_size = upper - mid;
        int left_array[left_array_size], right_array[right_array_size];
    
        int j = 0;
        for (int i = lower; i <= mid; i++) {
            left_array[j++] = array[i];
        }
        j = 0;
        for (int i = mid + 1; i <= upper; i++) {
            right_array[j++] = array[i];
        }
    
        // Performing merging in a non-increasing manner and count inversion pairs..
        int i = 0, k;
        j = 0;
        int resultIntermediate = 0;
        for (k = lower; k <= upper; ) {
            if (left_array[i] <= right_array[j]) {
                array[k++] = left_array[i++];
                if (i >= left_array_size)   break;
            }
            else {
                array[k++] = right_array[j++];
    
                // If a element in left_array_size is greater than an element from
                // right_array_size then rest of all other elements will also be
                // greater than that element of right_array_size because both
                // subarrays are sorted in non-decreasing order.
                resultIntermediate += left_array_size - i;
    
                if (j >= right_array_size)  break;
            }
        } //end of for loop.
    
    
        // Performing merging if i or j doesn't reach to its
        // maximum value i.e. size of the subarrays.
        while (i < left_array_size) {
            array[k++] = left_array[i++];
        }
        while (j < right_array_size) {
            array[k++] = right_array[j++];
        }
    
        // Returning the result...
        return resultIntermediate;
    
    } //end of the merge function.
    

    And function to count inversion pair

    int countInversionPair(int *array, int lower, int upper) {
        int count_inv_pair = 0;
        // Do recusion untill the problem / array can be subdevided.
        if (lower < upper) {
    
            // Partition the Array into two subproblems.
            int mid = (lower + upper) / 2;
    
            // Call the countInversionPair() function for these two
            // subarrays / subproblems recursively to count number of
            // inversion for these subproblems / subarrays.
            count_inv_pair = countInversionPair(array, lower, mid);
            count_inv_pair += countInversionPair(array, mid + 1, upper);
    
            // Merge these two subarrays into a sigle array
            count_inv_pair += merge(array, lower, mid, upper);
        }
        return count_inv_pair;
    }
    

    Now you can get number of inversion pair by calling the function from main as:

    int count_inv_pair = countInversionPair(array, 0, size - 1);
    

    And now you will get your answer..