在《算法导论》第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;
}