闲来无事,复习复习经典的算法导论。看到了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 );
}
}