1. 程式人生 > >排序演算法之歸併排序

排序演算法之歸併排序

歸併排序

        歸併排序(MergeSort)是建立在歸併操作上的一種有效的排序演算法,該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用。 

        分治法,也可以稱為分治策略:是將一個大規模的問題(原問題)劃分成n個規模較小而結構與原問題相似的子問題,遞迴地解決這些子問題,然後再合併其結果,就得到原問題的解。

        分治模式在每一層遞迴上都有三個步驟:分解、解決、合併。

        簡單來說歸併排序類似於對兩個有序序列的合併。

基本思想

        將待排序元素序列分成兩個長度相等的子序列,對每一個子序列排序,然後將他們合併成一個序列,合併兩個子序列的過程稱為二路歸併。

    歸併排序的核心步驟:  ●分組   ●歸併

      圖解:

      1.分組

        

        在這樣分組完畢之後,每個組內元素都只剩一個,那麼一個元素一定是有序的。這時候對相鄰的兩個元素進行歸併排序。兩兩排序完畢後再與其相鄰組繼續歸併,直至結束。           

       2.歸併    

                             

程式碼實現

    ●遞迴版本:

void MergeData(int arr[], int beg, int mid, int end, int* tmp)
{
	int cur1 = beg;
	int cur2 = mid;
	int index = beg;
	while (cur1 < mid && cur2 < end)
	{
		if (arr[cur1] < arr[cur2])
			tmp[index++] = arr[cur1++];
		else
			tmp[index++] = arr[cur2++];
	}
	while (cur1 < mid)
	{
		tmp[index++] = arr[cur1++];
	}
	while (cur2 < end)
	{
		tmp[index++] = arr[cur2++];
	}
}
void _MergeSort(int arr[], int beg, int end, int* tmp)// 分組
{
	if (end - beg > 1)
	{
		int mid = beg + ((end - beg)>>1);
		_MergeSort(arr, beg, mid, tmp);
		_MergeSort(arr, mid, end, tmp);
		MergeData(arr, beg, mid, end, tmp);  //歸併函式
		memcpy(arr + beg, tmp + beg, (end - beg) * sizeof(int));// 將臨時區域內有序資料拷貝至原陣列

	}
}
void MergeSort(int arr[], int size)
{
	if (size <= 1)
		return;
	int* tmp = (int*)malloc(sizeof(int) * size);// 建立臨時區域存放歸併後的陣列
	if (NULL == tmp)
	{
		return;
	}
	_MergeSort(arr,0,size,tmp);
	free(tmp);
	tmp = NULL;
}

-----------------------------test.c----------------------------------
#include<assert.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

void test()
{
	int arr[] = { 9,5,2,7,4,3,8,6 };
    int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;

	MergeSort(arr, sz);

	for (; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
int main()
{
	test();
	system("pause");
	return;
}

     ●非遞迴版本

void MergeData(int arr[], int beg, int mid, int end, int* tmp)
{
	int cur1 = beg;
	int cur2 = mid;
	int index = beg;
	while (cur1 < mid && cur2 < end)
	{
		if (arr[cur1] < arr[cur2])
			tmp[index++] = arr[cur1++];
		else
			tmp[index++] = arr[cur2++];
	}
	while (cur1 < mid)
	{
		tmp[index++] = arr[cur1++];
	}
	while (cur2 < end)
	{
		tmp[index++] = arr[cur2++];
	}
}

void MergeSortNor(int arr[], int size)
{
	int gap = 1;
	int i = 0;
	int* temp  = (int*)malloc(sizeof(int) * size);
	if (NULL == temp)
	{
		return;
	}
	while (gap < size)
	{
		for (i = 0; i < size; i += 2 * gap)
		{
			int left = i;
			int mid = i + gap;
			int right = mid + gap;
			//計算左右兩半部分的區間


			if (mid > size)
				mid = size;
			if (right > size)
				right = size;
			//檢測左右半部分割槽間的合法性

			MergeData(arr, left, mid, right, temp);
			//歸併
		}
		memcpy(arr, temp, size * sizeof(arr[0]));
		gap <<= 1;
	}
	free(temp);
	temp = NULL;
}

------------------------------------test.c-----------------------------------------
#include<assert.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

void test()
{
	int arr[] = { 9,5,2,7,4,3,8,6 };
    int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;

	MergeSortNor(arr, sz);

	for (; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
int main()
{
	test();
	system("pause");
	return;
}

總結

       歸併排序演算法的穩定性:穩定

       歸併排序演算法的時間複雜度: O (N * log N)

       適用場景:在用於排序一個整體無序但各個元素之間相對有序的序列時效率很高(外部排序)