1. 程式人生 > >歸併排序的遞迴形式與非遞迴形式(C++版)

歸併排序的遞迴形式與非遞迴形式(C++版)

歸併排序的核心思想是分治法,即將待排序資料分成多個小塊,對每個小塊進行排序,然後在兩兩合併小塊,最終完成對整體的排序

時間複雜度是nlogn

輸入:25,12,17,21,15,48

結果:


遞迴實現:遞迴類似於對此方法的場景再現,即先對整體進行劃分,然後對劃分後的部分進行排序(對遞迴函式的理解可以認為是從上層向下層進入),排序好之後再進行合併(可以認為是從下層上層開始返回)程式碼如下:

//歸併排序
void MergeSort(int arr[],int length)
{
	if(length<=1||arr==NULL)return;
	MergeSortRecursion(arr,0,length-1);
}
//把檢查邊緣問題放在遞迴迴圈的外側
void MergeSortRecursion(int arr[],int low,int high)//high表示最高座標
{
	if(low==high)return;
	int mid=(low+high)/2;
	MergeSortRecursion(arr,0,mid);
	MergeSortRecursion(arr,mid+1,high);
	//歸併
	int *tmp=new int[high-low+1];
	//如果記憶體分配不足,兩側成分已排序好,直接返回
	if(tmp==NULL) return;
	int i=low;int j=mid+1;int curTmp=0;
	while(i<=mid&&j<=high)
	{
		if(arr[i]>arr[j])//升序
		{
			tmp[curTmp++]=arr[j++];
		}else
		{
			tmp[curTmp++]=arr[i++];
		}
	}
	if(i<=mid)
	{
		for(;i<=mid;i++)
			tmp[curTmp++]=arr[i];
	}
	if(j<=high)
	{
		for(;j<=high;j++)
			tmp[curTmp++]=arr[j];
	}
	//返還給arr
	curTmp=0;
	for(int i=low;i<=high;i++)
		arr[i]=tmp[curTmp++];
	delete[] tmp;
}
非遞迴的實現是對遞迴形式的模擬,但是不能夠實現從上到下在由下到上的返回,因此我們之間利用從下向上返回的模擬,利用step一步一步上向上層爬,一直爬到最上層,具體程式碼如下:
//非遞迴
void MergeSortNonRecursion(int arr[],int length)//length表示陣列長度
{
	int *tmp=new int[length];
	int step=1;
	
	while(step<length)//由於步長小於length所以左側的必然存在
	{
		int i=0;//控制座標變化
		int left,right,leftToPos,rightToPos;
		while(i<length)
		{
			left=i;leftToPos=left+step-1;
			right=leftToPos+1;//保證剩下的部分不足low+step時用剩餘部分
			rightToPos=right+step-1>length-1?length-1:right+step-1;
			int p=left;int q=right;int k=left;
			while(p<=leftToPos&&q<=rightToPos)
			{
				if(arr[p]>arr[q])//升序
				{
					tmp[k++]=arr[q++];
				}else
				{
					tmp[k++]=arr[p++];
				}
			}
			if(p<=leftToPos)
			{
				while(p<=leftToPos)
					tmp[k++]=arr[p++];
			}
			if(q<=rightToPos)
			{
				while(q<=rightToPos)
					tmp[k++]=arr[q++];
			}
			//返還給arr
			for(int j=left;j<=rightToPos;j++)
			{
				arr[j]=tmp[j];
			}
			i+=2*step;
		}
		step*=2;
	}
	delete[] tmp;
}
其中對於引數陣列進行說明:C++中傳遞引數分為傳值和傳址,其中基本資料型別是傳值即生成一個新的副本儲存在呼叫函式的棧空間中,因此對其進行內容修改不會對原資料造成資訊改動;當進行傳址引用的時候對呼叫函式內的該資料進行改動的時候可以修改原資料資訊。陣列作為引數進行傳遞的時候預設會退化成指標型別,此處相當於傳址所以可以改動原資料資訊。

陣列做引數退化成指標型別