归并排序

归并排序简单地将原始数组划分为两个子数组,然后对每个子数组递归的排序,最后再将有序子数组合并,归并排序的主要步骤为:
(1)将数组划分为两个子数组。 (2)分别对两个子数组递归进行合并排序。 (3)将这两个已经排好序的子数组合并为为一个有序的数组。
归并排序是一种基于分治法的排序。归并排序的总的时间代价为o(nlogn),空间复杂度为o(n)。
原始优化的归并排序:
#include<iostream>
#include<time.h>
using namespace std;

//两个有序的子数组,合并为一个有序的数组
void merge(int arr[], int arrTmp[], int start, int end, int middle)
{
	//将数组暂时存入临时数组中
	for(int i = start; i <= end; ++i)
	{
		arrTmp[i] = arr[i];
	}
	
	//左边数组的起始位置
	int index1 = start;
	//右边数组的起始位置
	int index2 = middle + 1;
	//原始数组的起始位置
	int pos = start;
	while(index1 <= middle && index2 <= end)
	{
		if(arrTmp[index1] < arrTmp[index2])
		{
			arr[pos++] = arrTmp[index1++];
		}
		else
		{
			arr[pos++] = arrTmp[index2++];
		}
	}

	while(index1 <= middle)
	{
		arr[pos++] = arrTmp[index1++];
	}

	while(index2 <= middle)
	{
		arr[pos++] = arrTmp[index2++];
	}
}


void mergeSort(int arr[], int arrTmp[], int start, int end)
{
	if(start < end)
	{
		int mid = (start + end) / 2;
		mergeSort(arr,arrTmp,start,mid);//对数组前半部分进行递归
		mergeSort(arr,arrTmp,mid + 1, end);//对数组后半部分进行递归
		merge(arr,arrTmp,start,end,mid);//进行归并
	}
}

int main(void)
{
	cout<<"请输入数组的大小";
	int num;
	cin>>num;
	int *arr = new int[num];
	int *arrTmp = new int[num]();
	for(int i = 0; i < num; ++i)
	{
		arr[i] = rand();
	}
	clock_t start,end;
	start = clock();
	
	mergeSort(arr,arrTmp,0,num - 1);

	end = clock();
	cout<<"排序时间:"<<float(end - start)/CLOCKS_PER_SEC*1000<<endl;

	system("pause");
	return 0;
}
实验结果:
请输入数组的大小:100000
排序时间:13
请输入数组的大小:1000000
排序时间:130
请输入数组的大小:10000000
排序时间:1550
请输入数组的大小:100000000
排序时间:16479
相同的数据集,归并排序比上一节中的快排要慢一点。
优化的归并排序:把数组暂时复制到临时数组时,将第二个数组中元素的顺序颠倒一下。这样,两个子数组从两端开始处理,向中间推进,使得这两个数组的两端互相成为另一个数组的监视哨。另一方面的优化是,当待排子数组中元素小于某个给定的值(50)时,则对子数组进行插入排序。
优化的归并排序:
#include<iostream>
#include<time.h>
using namespace std;

//优化的两个子数组归并,右数组逆置,都从两端向中间扫描,归并到新数组中。
void merge(int arr[], int arrTmp[], int start, int end, int middle)
{
	//复制左边的数组
	for(int i = start; i <= middle; ++i)
	{
		arrTmp[i] = arr[i];
	}
	
	//逆置复制右边的数组
	int i = middle + 1;
	int j = end;
	while(i <= end && j > middle)
	{
		arrTmp[i++] = arr[j--];
	}
	
	//数组归并到原数组中。
	int index1 = start;
	int index2 = end;
	int pos = start;
	while(index1 <= index2)
	{
		if(arrTmp[index1] < arrTmp[index2])
		{
			arr[pos++] = arrTmp[index1++];
		}
		else
		{
			arr[pos++] = arrTmp[index2--];
		}
	}
}

void insert_sort(int arr[], int n)
{
	for(int i = 1; i < n; ++i)
	{
		for(int j = i; j > 0; j--)
		{
			if(arr[j] < arr[j - 1])
			{
				swap(arr[j], arr[j - 1]);
			}
			else
			{
				break;
			}
		}
	}
}


void mergeSort(int arr[], int arrTmp[], int start, int end)
{
	if(end - start + 1 > 50)
	{
		int mid = (start + end) / 2;
		mergeSort(arr,arrTmp,start,mid);
		mergeSort(arr,arrTmp,mid + 1, end);
		merge(arr,arrTmp,start,end,mid);
	}
	else//当待排序的子数组中元素的个数小于50时,使用插入排序
	{
		insert_sort(arr + start, end - start + 1);
	}
}

int main(void)
{
	cout<<"请输入数组的大小:";
	int num;
	cin>>num;
	int *arr = new int[num];
	int *arrTmp = new int[num]();
	for(int i = 0; i < num; ++i)
	{
		arr[i] = rand();
	}
	clock_t start,end;
	start = clock();
	
	mergeSort(arr,arrTmp,0,num - 1);

	end = clock();
	cout<<"排序时间:"<<float(end - start)/CLOCKS_PER_SEC*1000<<endl;

	system("pause");
	return 0;
}
实验结果:
请输入数组的大小:100000
排序时间:19
请输入数组的大小:1000000
排序时间:121
请输入数组的大小:10000000
排序时间:1316
请输入数组的大小:100000000
排序时间:14791
优化以后的归并排序算法比原算法要快一些,但是,这种优化并没有从数量级上改善原来的算法时间代价仍然为o(nlogn),空间代价为o(n)。

你可能感兴趣的:(归并排序)