排序算法之歸併排序的遞歸與迭代實現方法

歸併排序簡述:

歸併排序是排序算法的一種新的思路,旨在把兩個或以上有序的數列歸併一個有序的數列,是爲歸併。

假如有一個含有n個元素的數組,將其看作n個有序列,然後兩兩歸併,最終得到一個有序列。

排序前的準備:

#define MAXSIZE 10
//順序表結構
template <class T>
struct SqList
{
	T s[MAXSIZE + 1] = { NULL, 98, 24, 55, 81, 32, 77, 48, 60, 14 ,8};          //數組s,s[0]用作哨兵
	int length = MAXSIZE;              //s長度
	void PrintArray();
};
typedef SqList<int> SList;

//交換l中的數組中下標爲i和j的值
void Swap(SList *L, int i, int j)
{
	int temp = L->s[i];
	L->s[i] = L->s[j];
	L->s[j] = temp;
}

template <class T>
void SqList<T>::PrintArray()
{
	for (int i = 1; i <= MAXSIZE; i++)
	{
		cout << s[i] << " ";
	}
	cout << endl;
}

準備了一個默認元素的數組用於排序。

遞歸實現歸併排序:

C++實現代碼如下:

//歸併排序主函數
void MSort(int array[], int res[], int m, int n)
{
	int pivot;                 //臨時變量
	int tempArray[MAXSIZE + 1];      //臨時數組    這裏的MAXSIZE默認爲10
	if (m == n)
	{
		res[m] = array[m];
	}
	else
	{
		pivot = (m + n) / 2;       //中間下標,把數組array平分爲[m...pivot]和[pivot+1...n]
		MSort(array, tempArray,m, pivot);         //先把數組[m...pivot]遞歸歸併到有序數組tempArray[m,pivot]
		MSort(array, tempArray, pivot + 1, n);    //把數組[pivot+1,n]遞歸歸併到有序數組tempArray[pivot+1,n]
		Merge(tempArray, res, m, pivot, n);//把有序數組tempArray[m,pivot]和有序數組tempArray[pivot+1,n]歸併爲一個新的有序數組
	}
}
這個函數的作用在於把一個無序數列,不斷遞歸分割爲兩半,最終得到n個單個元素,然後單個元素分別遞歸歸併成有序數列。

其中的Merge函數實現如下:

//歸併
//兩個有序數組歸併爲一個有序數組
void Merge(int tempArray[], int res[], int m, int pivot, int n)
{
	int i, r;       //臨時變量用於遍歷
	//不斷比較,將較小的數放到目標數組的前面
	for (i = pivot + 1,r = m;i <= n && m <= pivot; r++)   //i從pivot+1開始遍歷,r一開始等於m並作爲res數組的下標
	{
		if (tempArray[m]<tempArray[i])         //比較臨時數組的第1位[m]和[pivot+1]位的元素大小
		{
			res[r] = tempArray[m++];         //小於的話,就把m下標元素放在res第一位,並且m+1
		}
		else
		{
			res[r] = tempArray[i++];
		}
	}
	//上述比較後,會有剩餘的數組元素
	//把剩餘數組元素複製到res數組
	if (m<=pivot)
	{
		for (int l = 0; l <= pivot-m; l++)
		{
			res[r + l] = tempArray[m + l];
		}
	}
	if (i<=n)
	{
		for (int l = 0; l <= n-i; l++)
		{
			res[r + l] = tempArray[i + l];
		}
	}
}
最後可以用一個函數來調用主函數,作爲最終的函數方便使用:

//歸併排序
void MergeSort(SList *L)
{
	MSort(L->s, L->s, 1, L->length);
}

迭代實現歸併排序:

迭代仍然使用到了上面的Merge函數,用於歸併。

//歸併排序迭代實現
void MergeSort2(SList *L)
{
	int *resA = new int[L->length];     //用於存放最終得到的有序表
	int pivot = 1;             //變量記錄有序表長度
	while (pivot < L->length)       //不斷循環直到有序表長度大於原表
	{
		MergeIterate(L->s,resA,pivot,L->length);
		pivot *= 2;       //自乘兩倍
		MergeIterate(resA,L->s,pivot,L->length);
		pivot *= 2;
	}
}

//將array[]中長度爲pivot的序列,兩兩進行歸併,結果放到res[]
void MergeIterate(int array[], int res[], int pivot, int length)
{
	int i = 1,j;
	while (i<= length-2*pivot+1)
	{
		Merge(array, res, i, i + pivot - 1, i + 2 * pivot - 1);
		i = i + 2 * pivot;
	}
	if (i<length - pivot + 1)
	{
		Merge(array, res, i, i + pivot - 1, length);
	}
	else
	{
		for (j = i; j <= length; j++)
		{
			res[j] = array[j];
		}
	}
}

歸併排序的時間複雜度:

歸併排序的最好最壞和平均時間複雜度都爲O(nlogn),迭代方法的空間複雜度爲O(n)

迭代方法的效率要比遞歸更高,並且歸併排序是一種穩定的的排序算法。

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