今天,我们一起来学习归并排序的非递归算法吧!
目录
一.优势
二.实现原理
三.代码实现
相比于递归算法,归并排序的非递归算法不用多次调用同一个函数,不会向递归算法一样因为函数嵌套调用次数太多而造成栈溢出。
其实,相比于递归的算法,非递归与之不同点就一个:在递归中我们通过递归到最底层(即两个数一组)进行排序,而非递归则是直接把数组分成两个数一组进行排序,这边排序完之后,再把数组分成四个一组排序,直到整个数组被分成两组,排序后就结束。
假设有一个数组:int a[6]
5 | 3 | 4 | 1 | 7 | 2 |
实现非递归的思路就是:先用一个参数记录5的下标,排序5和3后,参数跳到4,排序4和1,参数再跳到7,排序7和2。之后参数再到a[0]下标(参数=0),排序5、3、4、1,之后参数跳到7,排序7、2。最后,参数再到 a[0],排序整体即可。
由思路可知,我们想要单趟排序就要让参数从头开始跳,直到最后一个排序位置,这里可以用for循环来解决。
以第一趟排序(两个一组)为例:
int gap = 1,gap是用来记录一组左右两边各几个,这里gap 是说一组里左右两边都是1个。
int head = 0, head来记录下标,因为归并排序每一组里又是分左和右的(参看递归算法的单趟排序原理手把手教你归并排序(递归))。
之后设int left1 = head, left2 = head + gap , right1 = head + gap - 1, right2 = left2 + gap - 1。然后像递归算法中单趟排序即可。
排完之后,head要到下一个位置,即head要到4所在的位置,排序方法同上。
排完4和3后head再加到7所在位置排序。这里排完之后就要结束for循环。所以head结束的标志是head = tail(数组最后一个元素下标) - gap - 1。
所以for循环应该是:for(int head = 0; head < tail - gap; head += (gap* 2))
但这样只是一趟的,我们需要好多趟(两个一组、四个一组、八个一组...)所以for循环外要嵌套一个while循环,while循环是用来确定每一趟以几个为一组,可以理解为while循环用来改变gap的值的。每次for循环结束后gap *= 2即可。
大体框架如下:
int gap = 1;
while(gap < tail)
{
for(int head = 0; head < tail - gap; head += (gap*2))
........//for循环内容
gap*=2;
}
void mergesortnonr(int* a, int left, int right, int n)//归并排序非递归法
{
int* tmp = (int*)malloc(sizeof(int) * n);
int gap = 1;
while (gap < n)
{
for (int i = 0; i <= right; i = i + 2 * gap)
{
int begin1 = i, end1 = i + gap - 1, mid = i + gap - 1;
int begin2 = mid + 1, end2 = i + 2 * gap - 1;
int j = i;
if (begin2 > right) break;
if (end2 > right) end2 = right;
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++];
}
for (int j = i; j <= end2; j++)
{
a[j] = tmp[j];
}
}
gap = 2 * gap;
}
}
创作不易,恳请点赞评论支持一下吧 如有错误,敬请斧正