集計ソート-逆シーケンスアルゴリズムを求める


まとめて並べ替えてずっと比較的に簡単だと思って、特に重視しないで、今日書いてみて、とても気まずくて、何度も失敗しました.最後にやはり博の記録を書くことにしました.くだらない話は多く言わないで、直接本題に入ります.
アルゴリズム:集計ソートは分割法(分割して治める)の典型的な応用であり、再帰的な考え方を適用し、上から下へ考える:まずMergeSort()が1つの乱順の配列を並べ替えることができると仮定し、そのため「分割」(1つの配列を平均して2つの部分に分ける)を開始し、さらに「治」(それぞれ前後の部分に対してMergeSort()を呼び出して秩序化する)を開始することができる.最后にもう1つの合并子関数Merge()を书いて、それは2つの秩序ある配列を合并することができて、Merge()は実现しやすいです.ただ2つのポインタを管理して、それぞれ2つのサブ配列の初めを指して、新しいメモリを开いて中间の结果を保存して、2つの配列を遍歴して完成することができて、时间はΘ(n).
n個の要素の配列がMergeSort()を呼び出すのに時間T(n)がかかるとする.従って、T(n)=2 T(n/2)+Θ(n).主定理から分かる:T(n)=Θ(nlogn).集計ソートアルゴリズムの時間的複雑さはΘ(nlogn)、空間複雑度はΘ(n).
集計ソートにより拡張する逆シーケンス数を求めることができる.
逆シーケンス数の定義:iA[j]の場合.A[i]とA[j]は逆序数対である.逆序数対の個数を逆序数と呼ぶ.従って逆序数を求めるには2つのポインタ,2回の走査配列,蛮力法により求めることができ,明らかに時間的複雑度はΘ(n^2).では、もっと早い方法はありますか?答えは肯定的で、集計ソート法を利用して、少し改善すればよい.Merge()では、2つの整列した配列Aを合併するが、B.はA.Bが整列しているため、A,Bのそれぞれの逆シーケンス数は0であるため、ABの逆シーケンス数はA,Bの間の逆シーケンス数に等しい.
例えば、A=1,4,6,7,9 B=2,3,5,10,13,21.Mergeでは現在のi号元素の4が2より大きいことが発見する、4の逆序数は+1を必要とし、また6,7,9が4の後ろに並んでいるため、6,7,9の逆序数も+1すべきであるため、全体の逆序数にlast-i+1を加えるべきである.(i号元素がB[j]よりも小さい(例えば4対5より小さい)場合、逆序数の変化を特定することができず、何の変更もしない).
コード:
#include<iostream>
using namespace std;
int count=0;//  
void Merge(int* A,int left,int mid,int right,int* C)
{	//Merge             ,     :o(n)
	int i=left;
	int j=mid+1;
	int k=left;
	while(i <= mid && j <= right)
	{
		if(A[i] <= A[j])
			C[k++]=A[i++];
		else
		{
			C[k++]=A[j++];
			count += mid-i+1;//  
		}
	}
	while(i <= mid)
		C[k++]=A[i++];
	while(j <= right)
		C[k++]=A[j++];
	//C[]    , C[]         A[]
	for(int i=left;i <= right;++i)
		A[i]=C[i];
}	
void MergeSort(int* A,int left,int right,int* C)//  MergeSort        A   .
{
	if(left < right)
	{
		int mid=(left+right)/2;
		MergeSort(A,left,mid,C);//      1
		MergeSort(A,mid+1,right,C);//      2
		Merge(A,left,mid,right,C); //         
	}
}

void main()
{
	int A[]={2,1,3,6,4,0,11,3,5};
	int len=sizeof(A)/sizeof(A[0]);
	int *C=new int[len];
	MergeSort(A,0,len-1,C);
	for(int i=0;i<len;++i)
		cout<<A[i]<<' ';
	cout<<endl;
	cout<<count<<endl;
	delete[] C;
}