ある日、やかんでお湯を沸かしたとき
誤って水で満たされた
だからそれが沸騰するとき
水が出続けます
再帰によってスタックオーバーフローが発生した状況を思い出しました
そこで姉妹のアジはインターネットで非再帰的なアルゴリズムを学びました
次に、妹のアジが教えてくれます!
この号では、Sister Aziが、クイックソートとマージソートの非再帰的な実装について学習します。!!
コンテンツ:
1.クイックソートは再帰的ではありません
2.マージソートは非再帰的です
非再帰について学習する前に、非再帰の利点をよりよく理解するために、再帰の落とし穴について学習する必要があります。
再帰の欠陥:スタックフレームスペースが深すぎ、スタックスペースが十分でないため、オーバーフローが発生します。
C言語のメモリ割り当て:
例えば:
再帰的に1000回:
再帰的に10000回:
図:
再帰(関数呼び出し)は、スタックをプッシュする操作です。スタックを深くプッシュしすぎると、スタックオーバーフローが発生します。
再帰的から非再帰的方法:①ループを直接変更する②データ構造スタックを使用して再帰的プロセスをシミュレートする
1.クイックソートは再帰的ではありません
クイックソート掘削の非再帰的実装を使用してみましょう
1.1コードの実装
スタックの操作については、以前にAZiが投稿したブログ投稿「2022年には誰もスタックを理解できない」を読むことができます。
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include"stack.h"
//挖坑法 - 升序
int PartSort(int* a, int left, int right)
{
int begin = left;
int end = right;
int key = a[begin];
int pivot = begin;
while (begin < end)
{
while (begin < end && a[end] >= key)
{
--end;
}
a[pivot] = a[end];
pivot = end;
while (begin < end && a[begin] <= key)
{
++begin;
}
a[pivot] = a[begin];
pivot = begin;
}
pivot = begin;//当begin与end相遇,随便把begin和end的值给pivot
a[pivot] = key;
return pivot;
}
//快速排序非递归
void QuickSortNonR(int* a, int n)
{
ST st;
StackInit(&st);//初始化栈
StackPush(&st, n - 1);//入数组最后一个元素下标
StackPush(&st, 0);//入数组第一个元素下标
while (!StackEmpty(&st))//当栈不为空我们就进入循环
{
int left = StackTop(&st);//取出栈顶元素给left
StackPop(&st);//出栈 - 删除栈顶元素
int right = StackTop(&st);
StackPop(&st);
int keyIndex = PartSort(a, left, right);//使用挖坑法区间排序
//[left, keyIndex - 1] keyIndex [keyIndex + 1, right] - 分成子区间
if (keyIndex + 1 < right)//因栈后进先出的特性,所以先入右区间
{
StackPush(&st, right);
StackPush(&st, keyIndex + 1);
}
if (left < keyIndex - 1)
{
StackPush(&st, keyIndex - 1);
StackPush(&st, left);
}
}
StackDestory(&st);//销毁栈
}
int main()
{
int arr[10] = {3, 4, 2, 5, 1};
int sz = sizeof(arr) / sizeof(arr[0]);
QuickSortNonR(arr, sz);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
1.2アイデアの説明
上記のコードに基づいてアイデアを学びましょう
2.マージソートは非再帰的です
2.1コードの実装
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include"stack.h"
//归并排序之非递归
void MergeSortNonR(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int)* n);
if (tmp == NULL)
{
perror("malloc:");
return;
}
int gap = 1;//gap为每组数据的个数,每次翻倍
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;
//归并过程中右半区间有可能不存在!
if (begin2 >= n)
break;
//归并过程中右半区间越界了,就修正
if (end2 >= n)
{
end2 = n - 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 = i; j <= end2; ++j)
{
a[j] = tmp[j];
}
}
gap *= 2;
}
free(tmp);
}
int main()
{
int arr[] = {10, 6, 7, 1, 3, 9, 4, 2 };
int sz = sizeof(arr) / sizeof(arr[0]);
MergeSortNonR(arr, sz);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
2.2アイデアの説明