给出一个算法,它能用O(nlgn)的最坏运行时间,确定n个元素的任何排列中逆序对的数目。(提示:修改合并排序)
《算法导论》在这里直接提示修改合并排序,所以自然就是用到分治归并的思想,那么逆序对是出现在什么时候?出现多少呢?
我们知道归并排序是通过分治,然后归并两个有序的序列成一个有序序列。现假设两段有序序列分别是[beg,mid]和[mid+1,end],在归并过程中(i,j分别为两段序列的下标),如果a[i]<a[j],则不会产生逆序对;但当a[i]>a[j]时,就出现逆序对了,出现了多少?既然[beg,mid]是有序的,那么[i-mid]序列就都能与a[j]构成逆序对,故:mid-i+1
复杂度O(nlgn),代码如下:
#include<iostream> using namespace std; /* 归并求逆序对数, arr 存储最终有序结果 * 在函数外申请一个临时数组作为参数传入 * 避免递归不断创建临时数组的开销 */ int Merge(int * arr, int beg, int mid, int end, int * tmp_arr) { memcpy(tmp_arr+beg,arr+beg,sizeof(int)*(end-beg+1)); int i = beg; int j = mid + 1; int k = beg; int inversion = 0; // 合并过程中的逆序数 while(i <= mid && j <= end) { if(tmp_arr[i] <= tmp_arr[j]) { arr[k++] = tmp_arr[i++]; }else { arr[k++] = tmp_arr[j++]; inversion += (mid - i + 1); } } while(i <= mid) { arr[k++] = tmp_arr[i++]; } while(j <= end) { arr[k++] = tmp_arr[j++]; } return inversion; } int MergeInversion(int * arr, int beg, int end, int * tmp_arr) { int inversions = 0; // 记录倒序数 if(beg < end) { int mid = (beg + end) >> 1; inversions += MergeInversion(arr, beg, mid, tmp_arr); inversions += MergeInversion(arr, mid+1, end, tmp_arr); inversions += Merge(arr, beg, mid, end, tmp_arr); } return inversions; } /* 测试序列 :answer: 10 */ int testPoint[10] = { 1, 4, 2, 9, 48, 15, 13, 44, 6, 90 }; void main() { int arrcopy[10]; // 临时数组 memcpy(arrcopy,testPoint,sizeof testPoint); printf("the num of inversions is: %d\n", MergeInversion(testPoint,0,9,arrcopy)); }