算法導論——InversionNumbers

         這是算法導論第二章後面的思考題,問題描述:設A[1..n]是一個包含n個不同數的數組。如果在i<j的情況下,有A[i]>A[j],則(i,j)就稱爲A的一個逆序對(inversion)。例如 ( 2,3,8,6,1 ) 中包含5個逆序:(1,5),(2,5),(3,5),(3,4),(4,5).

        現在要求給出一個算法,能夠用O(nlgn)的最壞情況運行時間,確定n個元素的任何排列中逆序對的數目。(提示:修改歸併排序)


          由給出的提示,以及給出的時間複雜度O(nlgn)的要求,可以想到分治的策略進一步可以由主定理猜測,遞歸式爲T(n)=2T(n/2)+O(n)。其中T(n/2)表示子問題的規模,O(n)是合併子問題的開銷。

        和歸併排序是一個框架,大致如下:

        (1)將數組A分成兩個子數組;

        (2)分別求兩個子數組中的逆序;

        (3)將子數組合並起來,求整體的逆序;


         要注意的是,在第二步求子數組逆序時,兩個數組之間的逆序沒有考慮,這需要在合併的時候進行處理。若子數組left中的第i個大於子數組right中的第j個,這兩個數構成一個逆序。合併子數組時,子數組已經排好序,可知left中下標大於i的元素都大於right[j]。

         由歸併操作的流程,涉及left[i]和right[j]的歸併操作有且只有一次。合併之後,將按照升序一同出現在後續操作的同一個子數組中。因此,每次遇到left[i]>right[j]的時候,要將left[i]之後元素和right[j]構成的逆序一起進行統計。

       代碼如下:

public class CountInversion {

	static int countInver(int[] a,int l,int r)
	{
		int inversion=0;
		if(l<r){
			int mid=(l+r)/2;
			inversion+=countInver(a,l,mid);
			inversion+=countInver(a,mid+1,r);
			inversion+=mergeInver(a,l,mid,r);
		}
		return inversion;
	}
		
	static int mergeInver(int[] a,int l,int mid,int r)
	{
		int n1=mid-l+1;
		int n2=r-mid;
		int[] left=new int[n1];
		int[] right=new int[n2];
		for(int i=0;i<n1;i++)
			left[i]=a[l+i];
		for(int j=0;j<n2;j++)
			right[j]=a[mid+1+j];
		
		int inversion=0;
		
		int i=0,j=0,k=l;
		while(i<n1&&j<n2)
		{
			
			if(left[i]<right[j])
			{
				a[k++]=left[i++];
			}
			else
			if(left[i]>right[j])
			{
				inversion+=n1-i;    //[i~n1-1]的元素都大於right[j]
				a[k++]=right[j++];
			}
			else
			{
				a[k++]=right[j++];
				a[k++]=left[i++];
			}
		}
		while(i<n1)
			a[k++]=left[i++];
		while(j<n2)
			a[k++]=right[j++];
		return  inversion;
	}
	public static void main(String[] args)
	{
			int[] test1=new int[]{1,2,3,4,5,6,7};
			int[] test2=new int[]{6,5,4,3,2,1,0};
			int[] test3=new int[]{5,6,4,3,7,1,2};
			int ans1=countInver(test1,0,test1.length-1);
			int ans2=countInver(test2,0,test2.length-1);
			int ans3=countInver(test3,0,test3.length-1);
			System.out.println(ans1);
			System.out.println(ans2);
			System.out.println(ans3);
	}
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章