歸併排序的實現

       最近工作中要用到排序,當然要選擇O(nlgn)時間複雜度的算法,還要求是穩定的,週末就溫習了一下排序算法,歸併排序剛好能滿足。自己也想練練手,就先實現了簡單的對遞歸版本,之後實現了非遞歸的版本。它的空間複雜度爲O(n)。  因爲遞歸版本會使用函數堆棧,深度受到棧內存的限制,所以數據量不能太大,需要注意下。

//【歸併排序--遞歸版本】
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <assert.h>


void swap(int *a, int *b)
{
	int t = *a;
	*a = *b;
	*b = t;
}


void merge(int a[], int m, int n)
{
	int *b = (int*)malloc(sizeof(int)*n);
	assert(b != NULL);
	memcpy(b, a, sizeof(int)*n);

	int i = 0, j = m, k = 0;
	while(i < m && j < n)
	{
		if (b[i] <= b[j])
		{
			a[k++] = b[i];
			i++;
		}
		else
		{
			a[k++] = b[j];
			j++;
		}
	}

	if (i < m)
	{
		while (i < m) a[k++] = b[i++];
	}
	else
	{
		while (j < n) a[k++] = b[j++];
	}
}

void mergeSort(int a[], int n)
{
	if (n <= 1) return;
	if (n == 2) 
	{
		if (a[0] > a[1])
			swap(a+0, a+1);
		return;
	}

	int m = n/2;
	mergeSort(a, m);
	mergeSort(a + m, n - m);
	merge(a, m, n);
}



void setRandData(int a[], int n)
{
	srand(time(NULL));
	for (int i = 0; i < n; i++)
	{
		a[i] = rand();
	}
}

int main()
{
	const int N = 10000000;
	int *a = (int*)malloc(sizeof(int) * N);
	assert(a != NULL);

	setRandData(a, N);
	//for (int i = 0; i < N; i++)
	//{
	//	printf("%d\t", a[i]);
	//}
	printf("\n");

	clock_t t0 = clock();
	mergeSort(a, N);
	clock_t t1 = clock();
	printf("Take time %d ms\n", t1 - t0);


	for (int i = N/2; i < N && i < (N/2 + 200); i++)
	{
		printf("%d    ", a[i]);
	}
	system("pause");
	return 0;
}

//【歸併排序--非遞歸版本】
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <assert.h>

inline int min(int a, int b)
{
	return a < b ? a : b;
}


void setRandData(int a[], int n)
{
	srand(time(NULL));
	for (int i = 0; i < n; i++)
	{
		a[i] = rand() * rand();
	}
}

void mergeSort(int a[], int n)
{
	int i, j, t, start1, end1, start2, end2, len;
	int *b = (int*)malloc(sizeof(int) * n);
	assert(b != NULL);

	len = 1;	//len爲每次分塊的大小,即按照1,2,4,6,8,...,n增長
	while (len < n)
	{
		t = 0;
		i = 0;
		j = i + len;
		while (j != n)
		{
			//start1,end1,start2,end2用於表示進行比較的兩個數據塊下標,
			//座標範圍分別爲[start1,end1),[start2,end2)。
			start1 = min(n, i);
			end1   = min(n, i + len);
			start2 = min(n, i + len);
			end2   = min(n, i + len + len);

			i = start1;
			j = start2;
			while (i < end1 && j < end2)
			{
				if (a[i] <= a[j]) 
				{
					b[t++] = a[i++];
				}
				else
				{
					b[t++] = a[j++];
				}
			}

			if (i < end1)
			{
				while (i < end1)
					b[t++] = a[i++];
			}
			else
			{
				while (j < end2)
					b[t++] = a[j++];
			}
			//assert(i == end1 && j == end2 && t == end2);
			i = t;
		}
		len = min(n, len * 2);
		memcpy(a, b, sizeof(int) * n);
	}
	free(b);
}


int main()
{
	const int N = 100000000;
	int *a = (int*)malloc(sizeof(int) * N);
	assert(a != NULL);

	setRandData(a, N);
	//for (int i = 0; i < N; i++)
	//{
	//	printf("%d\t", a[i]);
	//}
	printf("\n");

	clock_t t0 = clock();
	mergeSort(a, N);
	clock_t t1 = clock();
	printf("Take time %d ms\n", t1 - t0);


	for (int i = N/2; i < N && i < (N/2 + 100); i++)
	{
		printf("%d    \n", a[i]);
	}
	system("pause");
	return 0;
}



【歸併排序-使用模板】

mergesort.h

#ifndef __MERGESORT_H
#define __MERGESORT_H


namespace MyNamespace
{
	typedef int (*Comparer)(const void *a, const void *b);

	template <typename T> 
	int defaultCompare(T *a, T *b)
	{
		if (*a == *b) return 0;
		return *a < *b ? -1 : 1;
	}

	template <typename T> 
	void arrayCopy(T *a, const T *b, int n)
	{
		while (--n >= 0) a[n] = b[n];
	}

	inline int minIndex(int a, int b)
	{
		return a < b ? a : b;
	}

	template <typename T>
	int mergeSort(T a[], int n, Comparer comparer)
	{
		T *b = NULL;
		int i, j, t, start1, end1, start2, end2, len;
		int cmpResult;

		b = new T[n];
		if (b == NULL) return -1;


		//len爲每次分塊的大小,即按照1,2,4,6,8,...,n增長
		len = 1;
		while (len < n)
		{
			t = 0;
			i = 0;
			j = i + len;
			while (j != n)
			{
				//start1,end1,start2,end2用於表示進行比較的兩個數據塊下標,
				//兩個分塊的下標範圍爲[start1,end1),[start2,end2)。
				start1 = minIndex(n, i);
				end1   = minIndex(n, i + len);
				start2 = minIndex(n, i + len);
				end2   = minIndex(n, i + len + len);

				//i,j分別表示當前進行比較的兩個元素的下標
				i = start1;
				j = start2;
				while (i < end1 && j < end2)
				{
					if (comparer != NULL)
						cmpResult = comparer(a+i, a+j);
					else
						cmpResult = defaultCompare(a+i, a+j);

					b[t++] = (cmpResult <= 0) ?  a[i++] : a[j++];
				}

				if (i < end1)
				{
					while (i < end1)
						b[t++] = a[i++];
				}
				else
				{
					while (j < end2)
						b[t++] = a[j++];
				}
				//assert(i == end1 && j == end2 && t == end2);
				i = t;
			}
			len = minIndex(n, len * 2);
			arrayCopy(a, b, n);
		}

		delete []b;
		b = NULL;
		return 0;
	}

	template <typename T>
	int mergeSort(T a[], int n)
	{
		return mergeSort(a, n, NULL);
	}

};

#endif //__MERGESORT_H



main.cpp

#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <iostream>
#include "mergesort.h"

using namespace std;

int myComparer(const int *a, const int *b)
{
		if (*a == *b) return 0;
		return *a < *b ? -1 : 1;
}

int myComparer2(const void *a, const void *b)
{
		if (*(int*)a == *(int*)b) return 0;
		return *(int*)a < *(int*)b ? -1 : 1;
}


struct S
{
	int a;
	string str;

	bool operator== (const S &o)
	{
		return (a == o.a) && (str == o.str);
	}

	bool operator< (const S &o)
	{
		if (a != o.a) return a < o.a;
		return str < o.str;
	}
};



int main()
{
	S ss[] = {
		{2, "ok"}, 
		{1, "hi"},
		{4, "aa"}
	};

	int a[] = {3,2,7,9,8};
	//MyNamespace::mergeSort(a, 5, (MyNamespace::Comparer)myComparer);
	MyNamespace::mergeSort(a, 5, myComparer2);
	printf("%d %d %d %d %d\n", a[0], a[1], a[2], a[3], a[4]);

	//
	//a[1] = 100;
	//MyNamespace::mergeSort(a, 5);
	//printf("%d %d %d %d %d\n", a[0], a[1], a[2], a[3], a[4]);

	MyNamespace::mergeSort(ss, 3);
	printf("%d,%s %d,%s\n", ss[0].a, ss[0].str.c_str(), ss[1].a, ss[1].str.c_str());

	system("pause");
	return 0;
}

輸出

2 3 7 8 9
1,hi 2,ok
請按任意鍵繼續. . .


發佈了44 篇原創文章 · 獲贊 15 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章