求数组中的逆序对

在《算法导论》第3版习题2-4中讲到了如何求数组中的逆序对

假设A[0..n-1]是一个有n个不同数的数组。
若 i < j 且 A[i]>A[j],则对偶 (i,j) 称为A的一个逆序对(inversion)。
给出一个确定在n个元素的任何排列中逆序对数量的算法(提示:修改归并排序)

题目已经提示修改归并排序可以完成,插入法是求逆序对的暴力解法,插入排序的次数就是逆序对的数目。
采用归并排序的分治策略:
将原始的数组平均分成两部分,如果左边部分 A[i] <= A[j],则不存在逆序对;
如果 A[i] > A[j],因为A中从A[i]往后的元素即 A[i] - A[n1 - i] 都与A[j]构成逆序对,所以逆序对的数量相应加上 n1 - i 。
因此只要在归并排序的基础上修改很少的代码即可。

实现如下:

int merge(int A[], int left, int mid, int right)
{
    int count = 0;

    int n1 = mid - left + 1;
    int n2 = right - mid;

    int *L = new int[n1];
    int *R = new int[n2];

    for (int i = 0; i < n1; ++i)
        L[i] = A[left + i];
    for (int j = 0; j < n2; ++j)
        R[j] = A[mid + 1 + j];

    int i = 0, j = 0;
    int k = left;
    while (i < n1 && j < n2)
    {
        if (L[i] <= R[j])
        {
            A[k++] = L[i++];
        }
        else
        {
            A[k++] = R[j++];
            // 一定不能遗漏这一行,左边子序列的数大于右子序列,则逆序对数为 n1 - i;
            count += n1 - i;
        }

    }

    while (i < n1)
        A[k++] = L[i++];
    while (j < n2)
        A[k++] = R[j++];

    delete L;
    L = NULL;

    delete R;
    R = NULL;

    return count;
}

int merge_sort(int A[], int left, int right)
{
    int count = 0;
    if (left < right)
    {
        int mid = left + (right - left) / 2;
        count += merge_sort(A, left, mid);
        count += merge_sort(A, mid + 1, right);
        count += merge(A, left, mid, right);
    }

    return count;
}

完整源代码放于github

猜你喜欢

转载自blog.csdn.net/tao_ba/article/details/80723867