Hello大家好,上次我们讲到了冒泡排序和快速排序,本文我们来讲一下排序算法中的归并排序,它是属于外排序的一种
首先我们来说一下什么是归并排序,对于归并排序,就是将已有的子序列合并,得到完全有序的序列。即先要将子序列有序,再使子序列段间有序,这样最终这个整体才能有序
void _MergeSort(int* a, int left, int right, int* tmp)
{
if (left >= right)
{ //若是左右两值不构成区间,则直接返回
return;
}
int mid = (left + right) >> 1; //去中间值——分为两个区间
//[left, mid][mid + 1, right] —— 左右分别递归
_MergeSort(a, left, mid, tmp);
_MergeSort(a, mid + 1, right, tmp);
//此时两侧已然后续,需使用二路归并将他们放到一个临时数组里
int begin1 = left, end1 = mid;
int begin2 = mid + 1, end2 = right;
int index = left;
//二路归并
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] < a[begin2])
{
tmp[index++] = a[begin1++];
}
else
{
tmp[index++] = a[begin2++];
}
}
/*
*此时跳出上面的循环后一定有一个区间已经结束
*但是还有一个区间没结束
*无需比较,直接放入另一数组即可
*/
while (begin1 <= end1)
{
tmp[index++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[index++] = a[begin2++];
}
//最后将临时数组拷回去
for (int i = left; i <= right; ++i)
{
a[i] = tmp[i];
}
}
void MergeSort(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int));
_MergeSort(a, 0, n - 1, tmp);
free(tmp);
}
第一模块已经实现了其原理的算法图,这里便不做详解,我们来跟着DeBug调试来测试一下代码
_MergeSort(a, 0, n - 1, tmp);
if (left >= right)
{ //若是左右两值不构成区间,则直接返回
return;
}
int mid = (left + right) >> 1; //去中间值——分为两个区间
//[left, mid][mid + 1, right] —— 左右分别递归
_MergeSort(a, left, mid, tmp);
_MergeSort(a, mid + 1, right, tmp);
重点来讲一下如何进行二路归并的部分⭐
//此时两侧已然后续,需使用二路归并将他们放到一个临时数组里
int begin1 = left, end1 = mid;
int begin2 = mid + 1, end2 = right;
int index = left;
//二路归并
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] < a[begin2])
{
tmp[index++] = a[begin1++];
}
else
{
tmp[index++] = a[begin2++];
}
}
/*
*此时跳出上面的循环后一定有一个区间已经结束
*但是还有一个区间没结束
*无需比较,直接放入另一数组即可
*/
while (begin1 <= end1)
{
tmp[index++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[index++] = a[begin2++];
}
//最后将临时数组拷回去
for (int i = left; i <= right; ++i)
{
a[i] = tmp[i];
}
了解了归并排序的递归实现,接下来我们来说一说它的非递归实现,这个的话校招可能会考到,但是平时的期末考试和考研是不会涉及到,大家需要的话继续往下看就行
我们首先来分析一下非递归实现归并排序是怎样一个思路
我们来看一下这个整体的思路
讲完了整体的整个思路,接下来来看看代码是怎么写的
void MergeSort_NoRecursive(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
int gap = 1;
while (gap < n)
{
for (int i = 0; i < n; i += gap * 2)
{
//[i, i + gap - 1][i + gap, i + 2 * gap - 1]
int begin1 = i, end1 = i + gap - 1;
int begin2 = i + gap, end2 = i + 2 * gap - 1;
int index = i;
//二路归并
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] < a[begin2])
{
tmp[index++] = a[begin1++];
}
else
{
tmp[index++] = a[begin2++];
}
}
/*
*此时跳出上面的循环后一定有一个区间已经结束
*但是还有一个区间没结束
*无需比较,直接放入另一数组即可
*/
while (begin1 <= end1)
{
tmp[index++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[index++] = a[begin2++];
}
}
//最后是将每组归并完后的数据全部拷贝回去,供下一次归并使用
for (int j = 0; j < n; ++j)
{
a[j] = tmp[j];
}
gap *= 2; //每次归并完gap * 2
}
free(tmp);
}
for (int i = 0; i < n; i += gap * 2)
//[i, i + gap - 1][i + gap, i + 2 * gap - 1]
int begin1 = i, end1 = i + gap - 1;
int begin2 = i + gap, end2 = i + 2 * gap - 1;
//最后是将每组归并完后的数据全部拷贝回去,供下一次归并使用
for (int j = 0; j < n; ++j)
{
a[j] = tmp[j];
}
看完了归并排序的递归实现非递归实现,你认为就结束了,不,对于归并排序,我们还需要考虑其边界值
//若是归并的右区间不存在
if (begin2 >= n)
break;
//若是归并过程中右半区间多计算了,则修正一下
if (end2 >= n)
end2 = n - 1;
//非递归
void MergeSort_NoRecursive(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
int gap = 1;
while (gap < n)
{
for (int i = 0; i < n; i += gap * 2)
{
//[i, i + gap - 1][i + gap, i + 2 * gap - 1]
int begin1 = i, end1 = i + gap - 1;
int begin2 = i + gap, end2 = i + 2 * gap - 1;
int index = i;
//若是归并的右区间不存在
if (begin2 >= n)
break;
//若是归并过程中右半区间多计算了,则修正一下
if (end2 >= n)
end2 = n - 1;
//二路归并
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] < a[begin2])
{
tmp[index++] = a[begin1++];
}
else
{
tmp[index++] = a[begin2++];
}
}
/*
*此时跳出上面的循环后一定有一个区间已经结束
*但是还有一个区间没结束
*无需比较,直接放入另一数组即可
*/
while (begin1 <= end1)
{
tmp[index++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[index++] = a[begin2++];
}
//最后是将每组归并完后的数据全部拷贝回去,供下一次归并使用
for (int j = i; j <= end2; ++j)
{
a[j] = tmp[j];
}
}
gap *= 2; //每次归并完gap * 2
}
free(tmp);
}
本文我们主要是讲解了归并排序,说到了其具有递归和非递归两种方法,普通的大家只需要掌握递归就可以了但是对于校招来讲,你要去掌握非递归。好,七大常用的排序算法说到这里就结束了,接下去会更新其他专栏,敬请期待
最后感谢您对本文的观看,如果问题请于评论区留言或者私信我