算法导论2.4 合并排序求逆序数

2-4 逆序对

    设A[1...n]是一个包含n个不同数的数组。如果在i<j的情况下,有A[i]>A[j],则(i,j)就称为A中的一个逆序对。

    (1)列出数组{2,3,8,6,1}的五个逆序。

    (2)如果数组的元素取自{1,2...,n},那么,怎样的数组含有最多的逆序对?

    (3)插入排序的时间与输入数组中逆序对的数量之间有怎样的关系?

    (4)给出一个算法,能用Θ(n㏒n)的最坏运行时间,确定n个元素的任何排列中你逆序对的数目。(提示:修改合并排序)

解答:

    (1) 略

    (2)逆序数组

    (3)插入排序的时间与输入数组中逆序对的数量呈线性正相关关系。通过观察插入排序的算法伪码可知,算法的运行步骤主要取决于内层循环中元素移动的次数,而每次移动就意味着数组的逆序数减一,当排序结束时逆序数为零。

    (4)依据分治法,如果我们将数组分解成两个子序列,分别求出两个子序列的逆序数,再求出两个子序列之间元素的逆序数,就可以得出整个数组的逆序数了。可以做以下考虑:

            分解:将问题分成前后两个规模为n/2的数组

            解决:分别求解各自的逆序对数。如果子问题规模为2或1,可直接求解。

            合并:此时虽然知道两个子序列各自的逆序对数,但两个子序列之间的逆序对数无法轻易获知,如果进行两两比较的话,合并操作的时间复杂度就是n2 ,分治法没有意义。

            再考虑上述“合并”的问题,如果此时两个子序列都是有序的话,则通过修改合并排序的MERG过程就可以得出子序列之间的逆序数:在MERG对两个子序列的第一个元素之间进行选则时,如果前一个序列的首元素被选中,则逆序数不变——该元素不会和后一个序列中的剩下元素构成逆序对,如果第二个序列的首元素被选中,则逆序数增加“第一个序列剩下的元素数”——该元素和前一序列中剩下的每个元素构成逆序对,MERG后这些逆序对消除。按着这个思路分治算法重新设计如下:

            分解:将问题分成前后两个规模为n/2的数组

            解决:分别进行递归合并排序,并记录累加排序所消除的的逆序对数。如果子问题规模为2或1,可直接求解。

            合并:通过合并排序的MERG进行合并,在MERG过程中按上述方法累加逆序数。

PS:在最初用分治法考虑问题(4)时,排序的作用在一开并不那么明显,但通过对“合并”的分析,要求对子问题的求解需要产生“排序”的副作用。这种”副作用“在分治法中是值得注意的。


代码:


import java.util.Arrays;

public class Test {
	public static int count = 0;

	public static void main(String[] args) {
		int arr[] = { 6, 5, 4, 3, 2, 1 };
		int[] result = sort_and_count(arr);
		System.out.println("逆序数:"+count);
		for(int i=0;i<result.length;i++){
			System.out.print(result[i]+" ");
		}
	}

	private static int[] sort_and_count(int[] arr) {
		if (arr.length == 1) {
			return arr;
		}
		int length = arr.length;
		int alength = length / 2;
		int A[] = Arrays.copyOfRange(arr, 0, alength);
		int B[] = Arrays.copyOfRange(arr, alength, length);
		A = sort_and_count(A);
		B = sort_and_count(B);
		arr = merge_and_count(A, B);
		return arr;
	}
	//此函数有两个功能:
	//(1)归并排序中的归并
	//(2)计算逆序数
	private static int[] merge_and_count(int[] a, int[] b) {
		int i = 0;
		int j = 0;
		int result[] = new int[a.length+b.length];
		int current = 0;
		while (i < a.length && j < b.length) {
			if(a[i]<b[j]){		
				result[current++] = a[i];
				i++;
			}
			if(a[i]>b[j]){
				result[current++] = b[j];
				count += (a.length - i);
				j++;
			}
		}
		if(i==a.length){	
			for(;j<b.length;j++){
				result[current++] = b[j]; 
			}
		}
		if(j==b.length){
			for(;i<a.length;i++){
				result[current++] = a[i]; 
			}
		}
		return result;
	}
}



你可能感兴趣的:(算法,n2,2010)