1. 程式人生 > >排序演算法之歸併排序 ( C語言版 )

排序演算法之歸併排序 ( C語言版 )

歸併排序 :(Merge Sort)是建立在歸併操作上的一種有效的排序演算法,該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱為二路歸併, 使用中犧牲空間換取時間的演算法 

歸併演算法核心步驟為 : 

  • 分解
  • 合併

  • 由於歸併排序不依賴於待排序元素序列的初始輸入狀態, 每次劃分時兩個子序列的長度基本一致, 因此歸併排序的最好, 最差好平均時間複雜度均為O(n*log2^n) , 是一種穩定的排序演算法
  • 歸併排序的優化,使用插入排序處理小規模的子陣列,一般可以將歸併排序的執行時間縮短10%~15%

程式碼實現如下 : 

//分組歸併
void _Merge(int *a, int begin1, int end1, int begin2, int end2, int *tmp)
{
	int index = begin1;
	int i = begin1, j = begin2;
	//注意:當劃分的區間足夠小時,begin1==end1,begin2==end2
	while (i <= end1&&j <= end2){
		if (a[i]<=a[j])
			tmp[index++] = a[i++];
		else
			tmp[index++] = a[j++];
	}
	//將左邊元素填充到tmp中
	while (i <= end1)
		tmp[index++] = a[i++];
	//將右邊元素填充的tmp中
	while (j <= end2)
		tmp[index++] = a[j++];
	//將tmp中的資料拷貝到原陣列對應的序列區間
	//注意:end2-begin1+1
	memcpy(a + begin1, tmp + begin1, sizeof(int)*(end2 - begin1 + 1));
}
//歸併排序
void MergeSort(int *a, int left, int right, int *tmp)
{
	if (left >= right)
		return;
	assert(a);
	//mid將陣列二分
	int mid = left + ((right - left) >> 1);
	//左邊歸併排序,使得左子序列有序
	MergeSort(a, left, mid, tmp);
	//右邊歸併排序,使得右子序列有序
	MergeSort(a, mid + 1, right, tmp);
	//將兩個有序子數組合並
	_Merge(a, left, mid, mid + 1, right, tmp);
}

完整程式碼 : 

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>

//分組歸併
void _Merge(int *a, int begin1, int end1, int begin2, int end2, int *tmp)
{
	int index = begin1;
	int i = begin1, j = begin2;
	//注意:當劃分的區間足夠小時,begin1==end1,begin2==end2
	while (i <= end1&&j <= end2){
		if (a[i]<=a[j])
			tmp[index++] = a[i++];
		else
			tmp[index++] = a[j++];
	}
	//將左邊元素填充到tmp中
	while (i <= end1)
		tmp[index++] = a[i++];
	//將右邊元素填充的tmp中
	while (j <= end2)
		tmp[index++] = a[j++];
	//將tmp中的資料拷貝到原陣列對應的序列區間
	//注意:end2-begin1+1
	memcpy(a + begin1, tmp + begin1, sizeof(int)*(end2 - begin1 + 1));
}
//歸併排序
void MergeSort(int *a, int left, int right, int *tmp)
{
	if (left >= right)
		return;
	assert(a);
	//mid將陣列二分
	int mid = left + ((right - left) >> 1);
	//左邊歸併排序,使得左子序列有序
	MergeSort(a, left, mid, tmp);
	//右邊歸併排序,使得右子序列有序
	MergeSort(a, mid + 1, right, tmp);
	//將兩個有序子數組合並
	_Merge(a, left, mid, mid + 1, right, tmp);
}
//列印陣列
void PrintArray(int *a, int len)
{
	assert(a);
	for (int i = 0; i < len; i++)
		printf("%d ", a[i]);
	printf("\n");
}
int main()
{
	int a[] = { 10, 6, 7, 1, 3, 9, 4, 2 };
	int *tmp = (int *)malloc(sizeof(int)*(sizeof(a) / sizeof(int)));
	memset(tmp, -1, sizeof(a) / sizeof(int));
	MergeSort(a, 0, sizeof(a) / sizeof(int)-1, tmp);
	PrintArray(a, sizeof(a) / sizeof(int));
	system("pause");
	return 0;
}

除錯結果如下 :