分治法求逆序数
前置知识:归并排序。
我们已经证明了归并排序的正确性。只要将归并排序算法作少量修改,就可以将求一个排列的逆序数的时间复杂度从O(n^2)降低到O(n logn)。
将一个排列P尽量均匀地分成两部分P_1,P_2。则P的逆序数
τ§=τ(P_1 )+τ(P_2 )+τ_Merge
显然,τ(P_1 )和τ(P_2 )分别代表递归求解的在P_1,P_2范围内的逆序数。而τ_Merge则将遗漏的情况,即构成逆序对(a_i,a_j), i<j, a_i>a_j的两个元素a_i,a_j满足a_i∈P_1,a_j∈P_2的情况全部覆盖。
归并排序的过程中,合并两个有序(升序)数列的步骤是:
设一个临时数列t,用于归并。
指针i和j分别指向两个子数列的开头。子数列的范围分别是_begin到_mid、_mid + 1到_end。
不断从两个子数列中各取一个元素(即指针i和j分别指向的元素)比较,将较小的数放到数列t中,对应的指针递增(当两个元素相等时,默认将地址较低的数组中的数,即i指向的数放入t)。当其中一个子数列取完后,该循环结束。
将没取完元素的子数列的全部元素都复制到数列t中。
设从长度分别为m,n的两个有序数列P_1,P_2中取出的两个数分别为x_i,y_j,下标代表它们在有序数列中的位置。每次比较时如果发现一次x_i>y_j,又已知序列是升序的,就有
x_k>y_j, k=i,i+1,…,m, x_k∈P_1,y_j∈P_2
也就是说找到了m-i+1个逆序对。即τ_Merge=m-i+1。
代码:
template<class _Ty> size_t merge(_Ty* _begin, _Ty* _mid, _Ty* _end) {
const