程序遞歸深度問題---爆棧的產生與解決


在函數調用過程中,反復調用自己的函數稱為遞歸函數。

如下面程序的函數調用過程為
(1) main里調用Hello
(2) Hello輸出”Hello”后繼續調用Hello函數
(3)一直這樣繼續
會發生什么?
沒完沒了一直到“爆棧”,也就是棧溢出,也即stackoverflow。
在windows的DEV-cpp編譯下你會看到

我們程序返回0代表程序正常結束,這個返回值代表程序已經爆棧

#include<bits/stdc++.h>
using namespace std;
void Hello()
{
	cout<<"Hello"<<endl;
	Hello();
}
int main()
{
	Hello();
	return 0;
}

我們可以記錄下遞歸的次數

#include<bits/stdc++.h>
using namespace std;
void Hello(int tot)
{
	cout<<"Hello"<<" "<<tot<<endl;
	Hello(tot+1);
}
int main()
{
	Hello(0);
	return 0;
}


我們也可以使用全局變量記錄下遞歸的次數

為什么兩個值都鎖定在了64890呢,多傳遞幾個參數呢,你會發現依舊到了這個神奇的數字,所以遞歸深度與傳遞的參數個數是無關的。
那這個64890究竟代表着什么呢,我們再來一個程序玩一下。

#include<bits/stdc++.h>
using namespace std;
int tot;

void test() 
{
	int buffer[1024];
	tot++;
	cout<<tot<<"\n";
	test();
}

int main() 
{
	test();
}

這個程序能運行503次,假設每次遞歸調用的內存開銷為x,那么這段程序的棧開銷為\(503*(4KB+x)\),一個int為4B,x為每次遞歸的開銷
他和\(64890x\)是相等的,\(64890x=503(4*1024+x)\),我們可以解出x的值為32,即每次遞歸開銷為32B,棧內存開銷為207360B,為2MB。
局部變量也是使用棧內存的,我們可以驗證一下,你可以在主函數內開辟\((1<<20)*1.9\)的數組,\(*2\)就正好超過了這個大小。查閱相關資料你也會知道window下編譯出的程序的棧缺省值(默認值)為2MB。
我們知道黑白圖像那個題必須要廣搜才能過,因為棧空間超過了大小,那么我把這個數據縮小到哪個數依舊可以過呢
假設這個圖的大小為N*N,我們每次遞歸需要32B的開銷,如果全是1,那么需要遞歸\(N*N\)次,我們可以求得N為256。256試一下發現不行,因為我們程序本身也需要一點點棧內存,要不然我們Hello連續遞歸運行次數應該為65536,但是確沒有達到,當然265就可以了。

#include <bits/stdc++.h>
using namespace std;
#define N 255
int n=N;
char s[1023][10223];
char vis[N][N];
int dir[4][2]={1,0,0,1,-1,0,0,-1};
int tx,ty,i;

void dfs(int x,int y)
{
    for(i=0;i<4;i++)
	{
		tx=x+dir[i][0];
		ty=y+dir[i][1];
		if(tx<0||tx>=n||ty<0||ty>=n||vis[tx][ty])continue;
		vis[tx][ty]=1;
		dfs(tx,ty);
	}	
}
int main()
{
    
    dfs(0,0);
    return 0;
}

如何解決呢,使用遞歸前一定要估計下遞歸的深度,深度在6e4以上請直接放棄遞歸,請使用非遞歸的形式實現。當然你也可以手動擴棧(🐶


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM