C語言非遞歸算法解決快速排序與歸并排序產(chǎn)生的棧溢出
建議還不理解快速排序和歸并排序的小伙伴們可以先去看我上一篇博客??????哦!C語言超詳細(xì)講解排序算法下篇
1、棧溢出原因和遞歸的基本認(rèn)識
我們先簡單來了解下內(nèi)存分布結(jié)構(gòu):

棧區(qū):用于存放地址、臨時變量等;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
堆區(qū):程序運(yùn)行期間動態(tài)分配所使用的場景;
靜態(tài)區(qū):存放全局變量和靜態(tài)變量,具體還分為 .bss段和.data段;
? ? ? ? ? ? ???.bss段:存放未初始化的和初始化為0的全局變量或者靜態(tài)變量;
? ? ? ? ? ? ???.data段:初始化不為0的全局變量或者靜態(tài)變量;
常量區(qū):存放常量(比如比變量名字,非0的初始化值,const常量,字符串等),只讀;
代碼區(qū):存放代碼的位置,只讀;
?我們再來看這樣的一串代碼運(yùn)行的結(jié)果:

這是一個累加求和的遞歸函數(shù),當(dāng)我們發(fā)現(xiàn)累加求和到1000遞歸仍然能正常執(zhí)行,我們接著改為10000看看是否還能正常運(yùn)行??

我們可以看到,當(dāng)數(shù)值達(dá)到10000的時候程序已經(jīng)崩了,并不會顯示任何錯誤,當(dāng)我們進(jìn)入調(diào)試可以發(fā)現(xiàn)報錯顯示棧溢出,那為什么會造成棧溢出呢,我們接著往下看!?
?遞歸的基本認(rèn)識:
?遞歸本質(zhì)也是函數(shù)調(diào)用,是函數(shù)調(diào)用,本質(zhì)就要形成和釋放棧幀
?調(diào)用函數(shù)是有成本的,這個成本就體現(xiàn)在形成和釋放棧幀上:時間+空間
?所以,遞歸就是不斷形成棧幀的過程
內(nèi)存和CPU的資源是有限的,也就決定了,合理的遞歸是絕對不能無限遞歸下去,如果遞歸調(diào)用深度太深,這樣有可能導(dǎo)致一 直開辟??臻g,最終產(chǎn)生??臻g耗盡的情況,這樣的現(xiàn)象我們稱為棧溢出!
既然使用遞歸極端情況下會出現(xiàn)棧溢出的問題,那么我們就用非遞歸的方式來實現(xiàn)快速排序和歸并排序!
2、快速排序(非遞歸實現(xiàn))
快速排序非遞歸實現(xiàn)思想:
首先我們可以借助數(shù)據(jù)結(jié)構(gòu)的棧來完成,遵循棧的后進(jìn)先出,我們可以先入右再入左,然后使用我們上一期講的三個方法中的其中一個方法,這里我們選擇挖坑法,使用挖坑法我們可以看作成兩個區(qū)間也就是: [left, keyIndex - 1] 和 [keyIndex + 1, right],如果區(qū)間存在我們接著入棧,如此循環(huán)直到棧為空,則排序結(jié)束!
圖解見下:

?代碼實現(xiàn)如下:
//挖坑法 - 升序
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;//當(dāng)begin與end相遇,隨便把begin和end的值給pivot
a[pivot] = key;
return pivot;
}
void QuickSortNonR(int* a, int n)
{
Stack st;
StackInit(&st);//初始化棧
StackPush(&st, n - 1);//入數(shù)組最后一個元素下標(biāo)
StackPush(&st, 0);//入數(shù)組第一個元素下標(biāo)
while (!StackEmpty(&st))//當(dāng)棧不為空我們就進(jìn)入循環(huán)
{
int left = StackTop(&st);//取出棧頂元素給left
StackPop(&st);//出棧 - 刪除棧頂元素
int right = StackTop(&st);
StackPop(&st);
int keyIndex = PartSort(a, left, right);//使用挖坑法區(qū)間排序
//[left, keyIndex - 1] keyIndex [keyIndex + 1, right] - 分成子區(qū)間
if (keyIndex + 1 < right)//因棧后進(jìn)先出的特性,所以先入右區(qū)間
{
StackPush(&st, right);
StackPush(&st, keyIndex + 1);
}
if (left < keyIndex - 1)
{
StackPush(&st, keyIndex - 1);
StackPush(&st, left);
}
}
StackDestory(&st);//銷毀棧
}
3、歸并排序(非遞歸實現(xiàn))
歸并排序非遞歸實現(xiàn)思想:
上期我們知道歸并需要開辟一個數(shù)組,并且使用分治的算法來實現(xiàn)歸并排序,而非遞歸版本我們的思路也是差不多,先讓他們一個一個歸并,然后兩個兩個歸并,再接著四個四個一起歸并,具體圖解見下:

?代碼實現(xiàn)如下:
void MergeSortNonR(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
if (tmp == NULL)
{
perror("malloc:");
return;
}
int gap = 1;//gap為每組數(shù)據(jù)的個數(shù),每次翻倍
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;
//歸并過程中右半?yún)^(qū)間有可能不存在!
if (begin2 >= n)
break;
//歸并過程中右半?yún)^(qū)間越界了,就修正下
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++];
}
//拷貝進(jìn)去
for (int j = i; j <= end2; ++j)
{
a[j] = tmp[j];
}
}
gap *= 2;
}
free(tmp);
}
本期到這里就結(jié)束了,相信你們已經(jīng)對非遞歸快速排序和歸并排序已經(jīng)很了解了,非遞歸這兩個在校招中經(jīng)常會考,加油把!
gitee(碼云):Mercury. (zzwlwp) - Gitee.com? ? ? ?
到此這篇關(guān)于C語言非遞歸算法解決快速排序與歸并排序產(chǎn)生的棧溢出的文章就介紹到這了,更多相關(guān)C語言 棧溢出內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
VC++ 字符串String MD5計算小工具 VS2008工程
基于字符串加密的MD5算法,VS2008 VC++,多字節(jié)編譯工程。主要代碼如下,實現(xiàn)了ANSI字符串加密與Unicode字符串加密,需要的朋友可以參考下2017-07-07
Qt物聯(lián)網(wǎng)管理平臺之實現(xiàn)數(shù)據(jù)查詢導(dǎo)出打印
這篇文章主要為大家介紹了如何利用Qt編寫物聯(lián)網(wǎng)管理平臺中數(shù)據(jù)查詢導(dǎo)出打印的功能,文字的示例代碼講解詳細(xì),感興趣的可以了解一下2022-07-07
C++ 關(guān)于MFC List Control 控件的總結(jié)
這篇文章主要介紹了C++ 關(guān)于MFC List Control 控件的總結(jié)的相關(guān)資料,十分的詳細(xì),有需要的朋友可以參考下2015-06-06
從string類的實現(xiàn)看C++類的四大函數(shù)(面試常見)
C++類一般包括構(gòu)造函數(shù)、拷貝構(gòu)造函數(shù)、析構(gòu)函數(shù)和賦值函數(shù)四大函數(shù),非常常見,本文給大家介紹從string類的實現(xiàn)看C++類的四大函數(shù),一起看看吧2016-06-06
Qt私有信號實現(xiàn)(private signal)
在使用Qt信號槽機(jī)制的時候,有時候我們需要一個信號只能由類內(nèi)發(fā)出,而不允許使用該類對象的用戶發(fā)出,此時就需要私有信號的支持,本文主要介紹了Qt私有信號實現(xiàn)(private signal),感興趣的可以了解一下2023-10-10

