闲来无事,复习复习经典的算法导论。看到了2-4,习题,计算逆序数的问题,忍不住实现了一下。
逆序数,是排列组合中常见的一个指标,可以用来衡量一个数列的杂乱成对(相对于顺序排列),在一些算法如水印算法中有广泛的应用。 如此如何快速的求得任意序列的逆
序数是关心的重点。常见的一种高效解法,是devid - and -conqure. 将序列平分成两段,分别计算逆序数,然后将两个有序的数列进行归并,并在归并过程中求取逆序数。两个
子序列的逆序数求取是较小规模的同样问题,可以用递归的方式完成。
简单说下如何在归并的过程中,计算逆序数。假设,有一个序列array[p, mid, r]. 其中array[p...mid], array[mid+1, ..., r]已经分别从小到大排好序。下面我们将两个子序列进行归
并。 假设当前的右边子序列(array[mid+1, ...., r])的当前待比较元素下表为right, 左边的为left, 当array[left] <= array[right], 这时候没有逆序发生(因为left的数 比right的
大)。当array[left] > array[right], 是,right指向的元素具有逆序数,个数为他之前的所有的数,即mid-left+1。如此遍历下去,即可得到在归并中得到逆序数。
算法非常简单直白。 复杂度为: T(n) = 2T(n/2) + O(n). 根据master定理, T(n) = O(nlogn). 空间复杂度为2n,当然可以更小,2logn。
附上java实现的源代码。
public class MergInversionCount { public static int count(int[] array, int p, int r) { int inversionCount = 0; if (p < r) { int mid = (p + r) / 2; inversionCount += count(array, p, mid); inversionCount += count(array, mid+1, r); inversionCount += mergeInversion(array, p, mid, r); } return inversionCount; } private static int mergeInversion(int[] array, int p, int mid, int r) { int inversionCount = 0; int[] temp = new int[r-p+1]; if(array.length < r) return inversionCount; int left = p; int right = mid + 1; int storeIndex = 0; while(left <= mid && right <= r) { if(array[left] > array[right]) { inversionCount += mid-left+1; //当前right存在逆序数,数目等于mid-left+1 temp[storeIndex] = array[right]; right++; } else { temp[storeIndex] = array[left]; left++; } storeIndex++; } if(left <= mid) { for(int i = left; i <= mid; i++) { temp[storeIndex] = array[i]; storeIndex++; } } if(right <= r) { for(int i = right; i <= r; i++) { temp[storeIndex] = array[i]; storeIndex++; } } for(int i = p; i <= r; i++) { array[i] = temp[i-p]; } return inversionCount; } } import static org.junit.Assert.*; import org.junit.Test; public class MergInversionCountTest { @Test public void testCount() { int[] array = {1,5,6,7,4}; int[] array2 = {1,5,6,7,4,3,11, 15, 13, 2, 8}; int inversionCount = MergInversionCount.count(array, 0, 4); assertTrue(inversionCount == 3 ); inversionCount = MergInversionCount.count(array2, 4, 10); assertTrue(inversionCount == 10 ); } }