專題:遞歸
1、定義:
一個函數直接或間接的調用自己2、遞歸滿足3個條件
1. 遞歸必須得有一個明確的終止條件2. 改函數所處理的數據規模必須在遞減
3. 這個轉化必須是可解的
3、循環和遞歸
遞歸:易於理解
速度慢
存儲空間大
循環:
不易理解
速度快
存儲空間小
4、應用
樹和森林就是以遞歸的方式定義的樹和圖的很多數算法都是以遞歸來實現的
很多數學公式就是以遞歸的方式定義的
斐波拉契序列
1 2 3 5 8 13 21 34
5.函數的調用:
當在一個函數的運行期間調用另一個函數時,在運行被調函數之前,系統需要完成三件事:
1. 將所有的實際參數,返回地址(函數執行完後要執行的下一條語句的地址)等信息傳遞給被調函數保存
2. 爲被調函數的局部變量(包括形參)分配存儲空間
3. 將控制轉移到被調函數的入口(開始執行被調函數)
從被調函數返回主調函數之前,系統也要完成三件事
1. 保存被調函數的返回結果
2. 釋放被調函數所佔的存儲空間
3. 依照被調函數保存的返回地址將控制轉移到調用函數(f調用前把下條語句的地址傳遞給它)
當有多個函數互相調用時,按照"後調用先返回"的原則,上述函數之間信息傳遞和控制轉移必須
藉助"棧"來實現, 即系統將整個程序運行時所需的數據空間安排在一個棧中,每當調用一個函數時,
就在棧頂分配一個存儲區,進行壓棧操作,每當一個函數退出時,就釋放它的存儲區,就行出棧操作,
當前運行的函數永遠都在棧頂位置
A函數調用A函數和A函數調用B函數在計算機看來是沒有任何區別的,只不過用我們日常的思維
方式理解比較怪異而已!
遞歸示例1:如何自己調用自己
#include<stdio.h>
void f(int n){
if(n==1)
printf("haha\n");
else
f(n-1);
}
int main(void){
f(10);
return 0;
}
遞歸示例2:1+2+3+...100的和
#include<stdio.h>
long sum(int n){
if(n == 1)
return 1;
else
return n +sum(n-1);
}
int main(void){
printf("%d\n",sum(100));
return 0;
}
遞歸示例3:階乘的遞歸實現
#include<stdio.h>
long f(long n){
if(n==1)
return 1;
else
return f(n-1) * n;
}
int main(void){
printf("%d\n",f(3));
return 0;
}
遞歸示例4:漢諾塔
#include<stdio.h>
void hannuota(int n, char A, char B, char C){
/*
如果是一個盤子
直接將x盤上的盤子從A移到C
否則
現將A柱子上的n-1個盤子藉助C移到B
直接將A柱子上的盤子從A移到C
現將B柱子上的n-1個盤子藉助A移到C
*/
if(1 == n){
printf("將編號爲%d的盤子直接從%c柱子移到%c柱子\n",n,A,C);
}
else{
hannuota(n-1,A,C,B);
printf("將編號爲%d的盤子直接從%c柱子移到%c柱子\n",n,A,C);
hannuota(n-1,B,A,C);
}
}
int main(void){
//定義三個盤
char ch1 = 'A';
char ch2 = 'B';
char ch3 = 'C';
int n;
printf("請輸入要移動盤子的個: ");
scanf("%d",&n);
hannuota(n,ch1,ch2,ch3);
return 0;
}