數據結構基礎七-----《遞歸專題》

專題:遞歸


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;
}
  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章