之前发了一期归并排序递归版本 这期讲解一下非递归版本 用循环模拟
有没有人好奇 既然有了递归版本 为何还要搞非递归版本 这不是多此一举吗?
其实不然 正因为递归是基于栈帧的基础上实现的 是不断在栈上开辟空间 所以总归有消耗
既然是开辟空间 如果递归深度太深 总有空间被占据完的时候 这就会导致栈溢出(StakeOverFlow)^~^
那么递归在这种情况之下显得很乏力 这就引出了今天的主角非递归循环实现归并排序
就是模拟归并排序 也是分为很多区间段去比较合并 但是从思路图上来看
归并排序呈现给人的是一个总分总的结构 先将一段完整的区间递归分为许多小的单元 在将小的单元回溯 成为原来的区间段
非递归排序呈现给人的是 将递归版本的前半段去掉 (递归部分去掉)只保留回溯部分
这只是从思路图上 简单的提取 因为两种方法的实现是不相同的 非递归只是模仿递归的实现
具体见代码怎样实现(当然这个代码肯定不是完整正确的代码 判断思路是这样的 但是会有一些边界问题 你能否看的出哪里出来问题 当然 你也可以去调试 观察begin1 end1 begin2 end2看是否有越界的问题)^~^
void MergeSort(int* arr, int n)
{
assert(arr);
//开辟一个tmp临时数组 存储排序之后的数据
int* tmp = (int*)malloc(sizeof(int) * n);
if (tmp == NULL)
{
printf("failed to allocate memory.");
exit(-1);
}
int gap = 1;//相当于从归并排序的最小单元向最大单元开始循环比较
while (gap < n)
{
for (int i = 0; i < n; i += 2 * gap)
{
//[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 (arr[begin1] > arr[begin2])
{
tmp[index++] = arr[begin2++];
}
else
{
tmp[index++] = arr[begin1++];
}
}
while (begin1 <= end1)
{
tmp[index++] = arr[begin1++];
}
while (begin2 <= end2)
{
tmp[index++] = arr[begin2++];
}
}
//将归并的数据拷贝回原数组
for (int j = i;j < n; j++)
{
arr[j] = tmp[j];
}
}
gap *= 2;
free(tmp);
tmp = NULL;
}
刚开始 gap == 1 为什么这样做?有没有人产生过这样的疑问。
在前面就大致介绍了递归和非递归的大致实现方式
相当于非递归就只模仿了递归回溯的那部分 仅此而已
由上图可以看出 会有越界现象 所以上面代码是不完整的
具体应该怎样修改呢?
首先我们应分析问题 再从问题出发解决问题
具体问题分为三种
1,end1越界,那么后面的[begin2,end2]肯定就不存在了
2,end1没越界,但区间[begin2,end2]不存在
3,区间[begin2,end2]内有数据 说明begin2存在 end2存不存在就带考究了 存在就不会出问题了
那只有不存在时才会出问题
有没有人问 难道begin1就不会出问题吗?当然不会 为什么呢?你也不看看begin1=i 那又怎么样呢
它是由for循环控制的 出界的话循环就会停止的 这就不需要你担心了
既然问题分析完了 就该解决问题了
void MergeSort(int* arr, int n)
{
assert(arr);
//开辟一个tmp临时数组 存储排序之后的数据
int* tmp = (int*)malloc(sizeof(int) * n);
if (tmp == NULL)
{
printf("failed to allocate memory.");
exit(-1);
}
int gap = 1;
while (gap < n)
{
for (int i = 0; i < n; i += 2 * gap)
{
//[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;
//end1越界 且 后面的区间不存在
if (end1 >= n)
{
end1 = n - 1;
}
//说明[begin2,end2]里面有数据 但是end2越界了
if (end2 >= n)
{
end2 = n - 1;
}
//既然你都不存在 那就让你这个区间不存在不就解决了吗
if (begin2 >= n)
{
begin2 = n;
end2 = n - 1;
}
int index = i;
while (begin1 <= end1 && begin2 <= end2)
{
if (arr[begin1] > arr[begin2])
{
tmp[index++] = arr[begin2++];
}
else
{
tmp[index++] = arr[begin1++];
}
}
//通过上面的循环 并不是所有的数据都被拷贝到 tmp数组 肯定有一区间段没有处理完
while (begin1 <= end1)
{
tmp[index++] = arr[begin1++];
}
while (begin2 <= end2)
{
tmp[index++] = arr[begin2++];
}
}
//将归并的数据拷贝回原数组
for (int j = i; j < n; j++)
{
arr[j] = tmp[j];
}
gap *= 2;
}
free(tmp);
tmp = NULL;
}
上面的处理是对越界的代码进行修正处理
将上面的代码进行的修正处理还可以简化 下面就是简化后的代码以及完整的代码
#include
#include
#include
void Print(int* arr, int n)
{
assert(arr);
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
void MergeSort(int* arr, int n)
{
assert(arr);
//开辟一个tmp临时数组 存储排序之后的数据
int* tmp = (int*)malloc(sizeof(int) * n);
if (tmp == NULL)
{
printf("failed to allocate memory.");
exit(-1);
}
int gap = 1;
while (gap < n)
{
for (int i = 0; i < n; i += 2 * gap)
{
//[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;
end1越界 且 后面的区间不存在
//if (end1 >= n)
//{
// end1 = n - 1;
//}
说明[begin2,end2]里面有数据 但是end2越界了
//if (end2 >= n)
//{
// end2 = n - 1;
//}
既然你都不存在 那就让你这个区间不存在不就解决了吗
//if (begin2 >= n)
//{
// begin2 = n;
// end2 = n - 1;
//}
// 核心思想:end1、begin2、end2都有可能越界
// end1越界 或者 begin2 越界都不需要归并
if (end1 >= n || begin2 >= n)
{
break;
}
// end2 越界,需要归并,修正end2
if (end2 >= n)
{
end2 = n - 1;
}
int index = i;
while (begin1 <= end1 && begin2 <= end2)
{
if (arr[begin1] > arr[begin2])//改升序或降序
{
tmp[index++] = arr[begin2++];
}
else
{
tmp[index++] = arr[begin1++];
}
}
//通过上面的循环 并不是所有的数据都被拷贝到 tmp数组 肯定有一区间段没有处理完
while (begin1 <= end1)
{
tmp[index++] = arr[begin1++];
}
while (begin2 <= end2)
{
tmp[index++] = arr[begin2++];
}
//将归并的数据拷贝回原数组
for (int j = i; j <= end2; ++j)
{
arr[j] = tmp[j];
}
}
gap *= 2;
}
free(tmp);
tmp = NULL;
}
int main()
{
int arr[] = { 8,9,6,5,7,4,1,2,3 };
int sz = sizeof(arr) / sizeof(arr[0]);
Print(arr, sz);
MergeSort(arr, sz);
Print(arr, sz);
return 0;
}
有什么问题可以留言私信~