C语言函数递归
函數遞歸
什么是遞歸
程序調用自身的編程技巧稱為遞歸(recursion)。遞歸作為一種算法在程序設計語言中廣泛應用。一個過程或函數在其定義或說明中有直接或間接調用自身的一種方法,它通常把一個大型復雜問題層層轉化為一個與原問題相似的規模較小的問題來求解,遞歸策略只需少量的程序就可以描述出解題過程所需要的多次重復計算,大大地減少了程序的代碼量。遞歸的主要思考方式在于:把大事化小。
遞歸的兩個必要條件
- 存在限制條件,當滿足這個限制條件的時候,遞歸便不再繼續。
- 每次遞歸調用之后越來越接近這個限制條件。
史上最簡單的遞歸
int main() {printf("hehe\n");main();return 0; }運行結果
什么是棧溢出呢?
我們的內存分為棧區,堆區和靜態區。棧區存放局部變量和函數參數,堆區存放動態開辟的內存,靜態區存放全據變量和Static修飾的變量。
代碼每調用一次函數就會在棧區為它分配新的空間,在函數遞歸的過程中每遞歸一次就在棧區開辟一份新空間。內存是有限的,像上面那樣不停的遞歸main函數,總有溢出的那一刻。
題目:接受一個整型值(無符號),按照順序打印它的每一位。例如:輸入:123,輸出:1 2 3
運行結果
主函數調用print函數傳入參數123,123大于9,除法運算后得12,12作為參數傳入新調用的print函數(這里內存為新調用的print函數開辟了一個新的空間,所以我在旁邊復制了一遍print函數),12大于9,除法運算后得1,1作為參數又傳入一個又新調用的print函數(這里內存為新調用的print函數開辟了一個新的空間,我又復制了一遍),比較大小后小于9進入printf函數,模運算打印輸出1,這時返回到上一個復制的print函數,執行printf函數,模運算打印輸出2。返回到最初的print函數,模運算打印輸出3。
題目:編寫函數不允許創建臨時變量,求字符串長度。
int my_strlen(char *str) {if (*str != '\0')return 1 + my_strlen(str + 1); //str+1地址加一elsereturn 0; }int main() {char arr[] = "bit";int len = my_strlen(arr); //arr是數組,數組傳參,傳過去的不是整個數組,而是首元素的地址printf("len =%d\n", len);return 0; }運行結果
遞歸與迭代
題目:求n的階乘(不考慮溢出)
迭代
int Fac1(int n) {int i = 0;int ret = 1;for (i = 1; i <= n; i++){ret *= i;}return ret; }int main() {int n = 0;int ret = 0;scanf("%d",&n);ret = Fac1(n);//循環的方式printf("%d\n",ret);return 0; }運行結果
遞歸
數學公式
int Fac2(int n) {if (n <= 1)return 1;elsereturn n * Fac2(n - 1); }int main() {int n = 0;int ret = 0;scanf_s("%d",&n);ret = Fac2(n);//循環的方式printf("%d\n",ret);return 0; }題目:求第n個斐波那契數。(不考慮溢出)
遞歸
int Fib(int n) {if (n <=2)return 1;elsereturn Fib(n - 1) + Fib(n - 2); }int main() {int n = 0;int ret = 0;scanf("%d", &n);ret = Fib(n);printf("ret = %d\n", ret);return 0; }但是我們發現有問題
- 在使用Fib這個函數的時候如果哦我們要計算第50個斐波那契數字的時候特別消耗時間。
- 使用Fib函數求10000的階乘(不考慮結果的正確性),程序會崩潰。
為什么?
我們發現Fib函數在調用的過程中很多計算其實在一直重復,我們把代碼改一下。
定義一個全局變量count來計算Fib函數運行多少次。
int count = 0; int Fib(int n) {if (n == 3) //用if判斷循環多少次{count++;}if (n <=2)return 1;elsereturn Fib(n - 1) + Fib(n - 2); }int main() {int n = 0;int ret = 0;scanf("%d", &n);ret = Fib(n);printf("ret = %d\n", ret);printf("count = %d\n", count);return 0; }運行結果
我這電腦求第50個斐波那契數會崩潰,求第40個斐波那契數,調用Fib函數三千九百萬次。
我們改用迭代。
迭代
int Fib(int n) {int a = 1;int b = 1;int c = 1;while (n > 2){c = b + c;a = b;b = c;n--;}return c; } int main() {int n = 0;int ret = 0;scanf("%d", &n);ret = Fib(n);printf("ret = %d\n", ret);return 0; }總結:
- 許多問題是以遞歸的形式進行解釋的,這只是因為它比非遞歸的形式更為清晰。
- 這些問題的迭代實現往往比遞歸實現效率更高,雖然代碼的可讀性稍微差些。
- 當一個問題相當復雜,難以用迭代實現時,此時遞歸實現的簡潔性便可以補償它所帶來的運行時開銷。
下一節數組
總結
- 上一篇: 基于SSM的社区疫情居民信息登记系统
- 下一篇: ProtoBuf(Google Prot