对于归并排序而言,我们应该需要关注一下思想,归并也是一种分治的思想,类似与快速排序,但是又和快速排序有本质的区别。
归并排序
归并排序将要排序的序列分成两个长度相等的子序列,然后对每一个子序列进行拆分,一直到只有两个元素的两个子序列,对这两个子序列进行归并,将它们合并成一个序列,这种方法就是所说的二路归并的方法。
实现归并排序的思路:
所以,综合下来,我们实现代码:
//递归解决归并排序
template<typename T>
void MergeSort1(T* arr, int n)
{
T* tmp = new T[n];
assert(tmp);
for (int i = 0; i < n; i++)
{
tmp[i] = arr[i];
}
merge_sort1(arr, tmp, 0, n - 1);
}
template<typename T>
void merge_sort1(T* arr, T* tmp, int begin, int end)
{
assert(arr);
assert(tmp);
if (begin == end)
return;
int mid = begin + ((end - begin) >> 1);
int begin1 = begin;
int end1 = mid;
int begin2 = mid + 1;
int end2 = end;
//递归过程
merge_sort1(arr, tmp, begin1, end1);
merge_sort1(arr, tmp, begin2, end2);
int t = begin;
//进行两个区间归并
while (begin1 <= end1&&begin2 <= end2)
{
if (arr[begin1] > arr[begin2])
{
tmp[t++] = arr[begin2++];
}
else
{
tmp[t++] = arr[begin1++];
}
}
//如果归并完后,有剩余
while (begin1 <= end1)
{
tmp[t++] = arr[begin1++];
}
while (begin2 <= end2)
{
tmp[t++] = arr[begin2++];
}
//转到原来的空间上
for (int i = 0; i <= end; i++)
{
arr[i] = tmp[i];
}
}
其实归并排序也可以进行优化的,1.利用插入排序优化归并排序
在归并中利用插入排序不仅可以减少递归次数,还可以减少内存分配次数(针对于原始版本)。
尽管合并排序最坏情况运行时间为o(nlgn),插入排序的最坏运行时间为o(n^2),但是插入排序的常数因子使得它在n较小时,运行要更快一些。因此,在合并排序算法中,当子问题足够小时,采用插入排序就比较合适了。
所以我们可以改为
template<typename T>
void MergeSort1(T* arr, int n)
{
T* tmp = new T[n];
assert(tmp);
for (int i = 0; i < n; i++)
{
tmp[i] = arr[i];
}
if (n>5)
merge_sort1(arr, tmp, 0, n - 1);
else
InsertSort(arr, n);
}
优化以后的归并排序的时间复杂度:O(nlogn)