简体   繁体   English

基于 CLRS 的归并排序 C++ 上的算法介绍,带反转计数

[英]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.我已经基于 CLRS Merge Sort 伪代码实现了一个计算倒数的合并排序,但答案不正确,没有对数组进行排序,也没有正确计算倒数。

Definition of inversion: Let A[1..n] be an array of n distinct whole numbers.反演的定义:令 A[1..n] 是一个由 n 个不同整数组成的数组。 If i < j and A[i] > A[j], then the pair (i,j) is called an inversion of A.如果 i < j 且 A[i] > A[j],则 (i,j) 对称为 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}.对于输入:{4,3,1,8,2} 和 5,正确答案是 6 次反转,排序数组是 {1,2,3,4,8}。 It returns 5 inversions and {4,4,4,3,4}.它返回 5 个反转和 {4,4,4,3,4}。

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.首先,在mergeInvCount方法的最后一个for中,arr是用基于1的索引访问的,所以它不起作用,修复它减去1以使用基于0的索引访问。

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.当左辅助数组上的元素大于右辅助数组上的元素时,我们必须为该数字计算 1 次反转,并为其后的每个其他元素计算 1 次反转,“Infinite”commodin 除外。 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.当左辅助数组从索引 1 开始时,它起作用,因为减法 (mid - i) 返回有序辅助数组中剩余的元素数,不考虑 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.但是当我们合并数组并且左边不是从 1 开始时,减法无法在数组中的实际索引之后返回正确数量的元素。

So the solution to this is to use n1, which stores the number of elements in the left auxiliar array.所以解决这个问题的方法是使用 n1,它存储左辅助数组中元素的数量。 This way, (n1 - i) returns the correct number.这样, (n1 - i) 返回正确的数字。

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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM