1. 程式人生 > >資料結構之排序(七)——歸併排序

資料結構之排序(七)——歸併排序

歸併排序(Merging Sort):假設初始序列含有n個記錄,則可以看成是n個有序的子序列,每個子序列的長度為1,然後倆倆歸併,得到[n/2]([x]表示不小於x的最小整數)個長度為2或1的有序子序列;再倆倆歸併,....,如此重複,直至得到一個長度為n的有序序列為止,這種排序方法稱為2路歸併排序。

原理


演算法

遞迴法

程式碼

#include "stdafx.h"
using namespace std;
#include<iostream>
#include"stdafx.h"
//用於要排序陣列個數最大值,可根據需要修改  
#define MAXSIZE 10  
typedef struct
{
	//用於儲存要排序陣列,r[0]用作哨兵或臨時變數  
	int r[MAXSIZE + 1];
	//用於記錄順序表的長度  
	int length;
}SqList;
//交換L中陣列r的下標為i和j的值  
void swap(SqList *L, int i, int j) {
	int temp = L->r[i];
	L->r[i] = L->r[j];
	L->r[j] = temp;
}

/* 將有序的SR[i..m]和SR[m+1..n]歸併為有序的TR[i..n] */
void Merge(int SR[], int TR[], int i, int m, int n)
{
	int j, k, l;
	for (j = m + 1, k = i; i <= m && j <= n; k++)	/* 將SR中記錄由小到大地併入TR */
	{
		if (SR[i]<SR[j])
			TR[k] = SR[i++];
		else
			TR[k] = SR[j++];
	}
	if (i <= m)
	{
		for (l = 0; l <= m - i; l++)
			TR[k + l] = SR[i + l];		/* 將剩餘的SR[i..m]複製到TR */
	}
	if (j <= n)
	{
		for (l = 0; l <= n - j; l++)
			TR[k + l] = SR[j + l];		/* 將剩餘的SR[j..n]複製到TR */
	}
}


/* 遞迴法 */
/* 將SR[s..t]歸併排序為TR1[s..t] */
void MSort(int SR[], int TR1[], int s, int t)
{
	int m;
	int TR2[MAXSIZE + 1];
	if (s == t)
		TR1[s] = SR[s];
	else
	{
		m = (s + t) / 2;				/* 將SR[s..t]平分為SR[s..m]和SR[m+1..t] */
		MSort(SR, TR2, s, m);		/* 遞迴地將SR[s..m]歸併為有序的TR2[s..m] */
		MSort(SR, TR2, m + 1, t);	/* 遞迴地將SR[m+1..t]歸併為有序的TR2[m+1..t] */
		Merge(TR2, TR1, s, m, t);	/* 將TR2[s..m]和TR2[m+1..t]歸併到TR1[s..t] */
	}
}

/* 對順序表L作歸併排序 */
void MergeSort(SqList *L)
{
	MSort(L->r, L->r, 1, L->length);
}
#define N 9
int main()
{
	int d[N] = {50,10,90,30,70,40,80,60,20};
	SqList L0;
	for (int i = 0; i <N; i++) {
		L0.r[i + 1] = d[i];
	}
	L0.length = N;
	cout<< "排序前:";
	for (int j = 1; j <= L0.length; j++) {
		cout<< L0.r[j]<<" ";
	}
	MergeSort(&L0);
	cout << "\n遞迴歸併排序後:";
	for (int j = 1; j <= L0.length; j++) {
		cout << L0.r[j]<<" ";
	}
	cout << endl;
	return 0;
}

執行結果

演算法複雜度

遞迴歸併的時間複雜度為:O(nlogn) 歸併排序是一種比較佔用記憶體,但卻效率高且穩定的演算法。

演算法穩定性

穩定

非遞迴法

程式碼

#include "stdafx.h"
using namespace std;
#include<iostream>
#include"stdafx.h"
//用於要排序陣列個數最大值,可根據需要修改  
#define MAXSIZE 10  
typedef struct
{
	//用於儲存要排序陣列,r[0]用作哨兵或臨時變數  
	int r[MAXSIZE + 1];
	//用於記錄順序表的長度  
	int length;
}SqList;
//交換L中陣列r的下標為i和j的值  
void swap(SqList *L, int i, int j) {
	int temp = L->r[i];
	L->r[i] = L->r[j];
	L->r[j] = temp;
}

/* 將有序的SR[i..m]和SR[m+1..n]歸併為有序的TR[i..n] */
void Merge(int SR[], int TR[], int i, int m, int n)
{
	int j, k, l;
	for (j = m + 1, k = i; i <= m && j <= n; k++)	/* 將SR中記錄由小到大地併入TR */
	{
		if (SR[i]<SR[j])
			TR[k] = SR[i++];
		else
			TR[k] = SR[j++];
	}
	if (i <= m)
	{
		for (l = 0; l <= m - i; l++)
			TR[k + l] = SR[i + l];		/* 將剩餘的SR[i..m]複製到TR */
	}
	if (j <= n)
	{
		for (l = 0; l <= n - j; l++)
			TR[k + l] = SR[j + l];		/* 將剩餘的SR[j..n]複製到TR */
	}
}


/* 非遞迴法 */
/* 將SR[]中相鄰長度為s的子序列兩兩歸併到TR[] */
void MergePass(int SR[], int TR[], int s, int n)
{
	int i = 1;
	int j;
	while (i <= n - 2 * s + 1)
	{/* 兩兩歸併 */
		Merge(SR, TR, i, i + s - 1, i + 2 * s - 1);
		i = i + 2 * s;
	}
	if (i<n - s + 1) /* 歸併最後兩個序列 */
		Merge(SR, TR, i, i + s - 1, n);
	else /* 若最後只剩下單個子序列 */
		for (j = i; j <= n; j++)
			TR[j] = SR[j];
}

/* 對順序表L作歸併非遞迴排序 */
void MergeSort(SqList *L)
{
	int* TR = (int*)malloc(L->length * sizeof(int));/* 申請額外空間 */
	int k = 1;
	while (k<L->length)
	{
		MergePass(L->r, TR, k, L->length);
		k = 2 * k;/* 子序列長度加倍 */
		MergePass(TR, L->r, k, L->length);
		k = 2 * k;/* 子序列長度加倍 */
	}
}
#define N 9
int main()
{
	int d[N] = {50,10,90,30,70,40,80,60,20};
	SqList L0;
	for (int i = 0; i <N; i++) {
		L0.r[i + 1] = d[i];
	}
	L0.length = N;
	cout<< "排序前:";
	for (int j = 1; j <= L0.length; j++) {
		cout<< L0.r[j]<<" ";
	}
	MergeSort(&L0);
	cout << "\n非遞迴歸併排序後:";
	for (int j = 1; j <= L0.length; j++) {
		cout << L0.r[j]<<" ";
	}
	cout << endl;
	return 0;
}

執行結果

演算法複雜度

非遞迴歸併排序的時間複雜度為:O(nlogn) 使用歸併排序時,儘量考慮用非遞迴方法。

演算法穩定性

穩定