Search code examples
c++arraysmergesortclrsinversion

Merge sort based on CLRS Introduction to algorithms, with inversion count, on C++


I have implemented a merge sort that counts inversions, based on CLRS Merge Sort pseudo-code, but the answer is not correct, doesn't sort the array and neither does it count the inversions correctly.

Definition of inversion: Let A[1..n] be an array of n distinct whole numbers. If i < j and A[i] > A[j], then the pair (i,j) is called an inversion of A.

I used pass by reference to work with the same vector.

int mergeSortInvCount(vector<int> &arr, int izq, int der);
int mergeInvCount(vector<int> &arr, int izq, int mitad, int der);

void invCountRecursivo(vector<int> &arr, int n){



    int numInversiones = mergeSortInvCount(arr, 1, n);
    cout << "Num inversiones:" << numInversiones << endl;
    for(int i=0; i < n; i++){

        cout<<arr[i]<<endl;
    }
}

int mergeSortInvCount(vector<int> &arr, int izq, int der){

    int invCount = 0;

    if(izq < der){

        int mitad = (izq + der)/2;

        invCount = mergeSortInvCount(arr, izq, mitad);
        invCount += mergeSortInvCount(arr, mitad+1, der);
        invCount += mergeInvCount(arr, izq, mitad, der);
    }

    return invCount;
}

int infinito = numeric_limits<int>::max();

int mergeInvCount(vector<int> &arr, int izq, int mitad, int der){

    int n1 = mitad - izq + 1;
    int n2 = der - mitad;

    int invCount = 0;

    vector<int> vectorIzq;
    vector<int> vectorDer;

    for(int k=0;k<n1;k++){

        vectorIzq.push_back(arr[izq+k-1]);
    }

    vectorIzq.push_back(infinito);

    for(int k=0;k<n2;k++){

        vectorDer.push_back(arr[mitad+k]);
    }

    vectorDer.push_back(infinito);

    int i = 0;
    int j = 0;

    for(int k = izq; k <= der; k++){

        if(vectorIzq[i] <= vectorDer[j]){

            arr[k] = vectorIzq[i];
            i++;
        }
        else{

            arr[k] = vectorDer[j];
            j++;
            invCount += (mitad - i);
        }
    }

    return invCount;
}

For input: {4,3,1,8,2} and 5, correct answer is 6 inversions, and sorted array is {1,2,3,4,8}. It returns 5 inversions and {4,4,4,3,4}.


Solution

  • Well, months has passed, and though I did make work the code implementation to return the sorted array, there was still an error on the inversions count. Today I worked on it and solve it, so here it is.

    First, in the last for of mergeInvCount method, arr is acessed with 1-based index, so it doesn't work, fixed it substracting 1 to access with 0-based index.

    Second, in the conditional that compares the two auxiliar arrays to merge, the case when we must count an inversions isn't correct.

    When the element on the left auxiliar array is greater than the element on the right auxiliar array, we must count 1 inversion for that number and 1 for each of the other elements after it, except the "Infinite" comodin. Since the auxiliar arrays are ordered due to recursive calls, it's correct.

    It works when the left auxiliar array begins at index 1, because the subtraction (mid - i) returns the number of elements that are left in the ordered auxiliar array, not accounting for the comodin.

    But when we merge arrays and the left doesn't begin at 1, the subtraction fails to return the correct number of elements after the actual index in the array.

    So the solution to this is to use n1, which stores the number of elements in the left auxiliar array. This way, (n1 - i) returns the correct number.

    Here is the working code:

    int mergeSortInvCount(vector<int> &arr, int izq, int der);
    int mergeInvCount(vector<int> &arr, int izq, int mitad, int der);
    
    void invCountRecursivo(vector<int> &arr, int n){
    
        int numInversiones = mergeSortInvCount(arr, 1, n);
        cout << "Num inversiones:" << numInversiones << endl;
        cout << "Ordered array, ascendant order" << endl;
        for(int i=0; i < n; i++){
            cout<<arr[i]<<endl;
        }
    }
    
    int mergeSortInvCount(vector<int> &arr, int izq, int der){
    
        int invCount = 0;
    
        if(izq < der){
    
            int mitad = (izq + der)/2;
            invCount = mergeSortInvCount(arr, izq, mitad);
            invCount += mergeSortInvCount(arr, mitad+1, der);
            invCount += mergeInvCount(arr, izq, mitad, der);
        }
    
        return invCount;
    }
    
    int infinito = numeric_limits<int>::max();
    
    int mergeInvCount(vector<int> &arr, int izq, int mitad, int der){
    
        int n1 = mitad - izq + 1;
        int n2 = der - mitad;
    
        int invCount = 0;
    
        vector<int> vectorIzq;
        vector<int> vectorDer;
    
        for(int k=0;k<n1;k++){
    
            vectorIzq.push_back(arr[izq+k-1]);
        }
    
        vectorIzq.push_back(infinito);
    
        for(int k=0;k<n2;k++){
    
            vectorDer.push_back(arr[mitad+k]);
        }
    
        vectorDer.push_back(infinito);
    
        int i = 0;
        int j = 0;
    
        for(int k = izq; k <= der; k++){
    
            if(vectorIzq[i] <= vectorDer[j]){
    
                arr[k-1] = vectorIzq[i];
                i++;
            }
            else{
    
                arr[k-1] = vectorDer[j];
                j++;
                invCount += (n1 - i);
            }
        }
    
        return invCount;
    }
    
    int main(){
    
        vector<int> v = {4,3,1,8,2};
        invCountRecursivo(v, 5);
        // Returns 6, the correct # of inversions of A
    
        return 0;
    }