在函數調用過程中,反復調用自己的函數稱為遞歸函數。
如下面程序的函數調用過程為
(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以上請直接放棄遞歸,請使用非遞歸的形式實現。當然你也可以手動擴棧(🐶