目录
前面快排的非递归版我们用了栈来实现,这里我们还能用栈实现吗?
快速排序可以用栈
- 快速排序的递归:前序递归
- 快速排序的非递归:借用栈
- 快排没有最后回归的过程,所以栈空就意味着结束。
而归并排序不一样。
- 归并排序的递归:后序递归
- 归并排序的非递归:直接分解
- 归并排序回归需要处理,而借助栈模拟非递归,根本没有回归这个过程。
借助栈模拟非递归,比较适合前序,后序需要复杂处理是不适合的。
基本思路
- 利用斐波那契数列的思想
- 顺着走
- 一一合并,二二合并,四四合并...
- 每小组合并之后拷贝回原数组(❗不要在每大组合并完再去拷贝❗)
- 设置一个gap表示归并每组的数据个数
- 直接实行合并的过程。往回走
每一组
- 设置【begin1,end1】和【begin2,end2】,控制两个区间的序列的合并
- 注意第一组begin1=i end1=i+gap-1 gap表示归并每组的数据个数,最后-1是因为end1表示的是下标
- 第二组是begin2=i+gap end2=i+2*gap-1
- i+=2*gap
整体
- 一一归,二二归,四四归通过一个循环进行控制,gap=gap*2
- 结束条件:gap< n
问题
最后会有越界?
- 合并的组数不一定都是2的次方倍,越界问题。(可以尝试打印区间来查看越界问题)
- 越界问题存在三种情况(begin1=i<n不会越界)
- end1>=n(后面两个肯定越界,第一序列存在数,第二序列不存在数)break;
- begin2>=n(end2肯定越界,第二序列不存在数)break;
- end2>=n(可能第二序列区间还存在数)要修改处理为end2 = n - 1;
代码实现
void MergeSortNonR(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
if (tmp == NULL)
{
perror("malloc fail");
return;
}
int gap = 1;
while (gap < n)
{
//printf("gap:%2d->", gap);
for (size_t i = 0; i < n; i += 2 * gap)
{
int begin1 = i, end1 = i + gap - 1;
int begin2 = i + gap, end2 = i + 2 * gap - 1;
// [begin1, end1][begin2, end2] 归并
//printf("[%2d,%2d][%2d, %2d] ", begin1, end1, begin2, end2);
// 边界的处理
if (end1 >= n || begin2 >= n)
{
break;
}
if (end2 >= n)
{
end2 = n - 1;
}
//printf("[%2d,%2d][%2d, %2d] ", begin1, end1, begin2, end2);
int j = begin1;
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] < a[begin2])
{
tmp[j++] = a[begin1++];
}
else
{
tmp[j++] = a[begin2++];
}
}
while (begin1 <= end1)
{
tmp[j++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[j++] = a[begin2++];
}
memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));
}
//printf("\n");
gap *= 2;
}
free(tmp);
}
时间复杂度
时间复杂度:O(N*logN)
归并排序——外排序
- 内部排序:数据元素全部放在内存中的排序。
- 外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。(磁盘)
- 我们前面学过的排序都是内排序
- 而归并排序既是内排序,也是外排序。
- 为什么数据要放到硬盘里面?
- 内存和硬盘的区别?
- 内存容量小,速度快,价格贵,带电存储。
- 硬盘容量大,速度慢,价格便宜,不带电存储,永久存储。
- 大量的数据在文件中保存,如果用归并排序使其有序?
外排序视频