43 遞迴的思想與應用1

2021-08-20 08:04:24 字數 4045 閱讀 7478

遞迴是一種數學上分而自治的思想

將原問題分解為規模較小的問題進行處理。

分解後的問題與原問題的型別完全相同,但規模較小。通過小規模問題的求解,能夠輕易求得原問題的解。

問題的分解是有限的(遞迴不能無限進行):當邊界條件不滿足時,分解問題(遞迴繼續進行),當邊界條件滿足時,直接求解(遞迴結束)。

地規模型的一般表示法:

f(n)=an並f(n-1)  n>1, a1 n==1

遞迴在程式設計中的應用

遞迴函式:函式體中存在自我呼叫的函式。

遞迴函式必須有遞迴出口(邊界條件)。

函式的無限遞迴將導致程式奔潰。

遞迴思想的應用:

求解sum(n)=1+2+3+..+n

sum(n)=n+sum(n-1) n>1

1       n==1

unsigned int sum(unsigned int n)

else

斐波拉切數列:

數列自身遞迴定義:1,1,2,3,5,8,13,21,...

fac(n)=fac(n-1)+fac(n-2)  n>=3 

1 n==2

1 n==1

unsigned int fac(unsigned int n)

if((n==2)||(n==1))

return 0;

遞迴呼叫時不要陷入遞迴執行的細節,建議遞迴模型,不要陷入流程。

用遞迴方法求字串長度:

strlen(s)=1+strlen(s+1)  *s!='\0'  

0   *s=='\0'

unsigned int strl(const char* s)

else

}小結:遞迴是將問題分而自治的思想,用遞迴解決問題首先要建立遞迴的模型,遞迴解法必須要有邊界條件,否則無解,不要陷入遞迴函式的執行細節,學會通過**描述遞迴問題。

44、單鏈表的轉置

reverse(list)=

guard=list->next; ret=reverse(list->next); guard->next=list; list->next=null; len(list)>=2;

guard=list  len(list)==1;

guard=null  list==null;

先找出口:分解成容易解決的小問題

node* reverse(node* list)

else

單向鍊錶的合併:

list->1-2-3->null

list->2-3-4->null

遞迴出口:

list2  list1==null

list1  list2==null

遞迴分解:

list1->valuevalue =>list1->next=merge(list1->next,list2)

list1->value>=list2->value =>list2->next=merge(list1,list2->next)

//合併

node* merge(node* list1,node* list2)

else if(list2==null)

else if(list1->valuevalue)

else

3漢諾塔問題:

將木塊借助b柱由a柱移動到c柱,每次只能移動乙個木塊,只能出現小木塊在大木塊之上。

分解:將n-1個木塊借助c柱由a柱移動到b柱,將最底層的唯一木塊直接移動到c柱,將n-1個木塊借助a柱由b柱移動到c柱

//漢諾塔問題

void hanoitower(int n,char a,char b,char c)//n個木塊由a移動到c

else

4全排列問題

->abc,acb,bac,bca,cab,cba

permutation(n)=

找到n-1個元素全排列的方法,就能得到n個元素的全排列  n>2

1個元素的全排列為自身 n==1

void permutation(char* s,char* e)

else}}

找出口,問題分解。

45、函式呼叫過程回顧:

程式執行後有乙個特殊的記憶體區供函式呼叫使用:

程式中的棧區:一段特殊的專用記憶體區

例項分析:逆序列印單鏈表中的偶數結點

list->1-2-3->null

r_print_even(list)=

r_print_even(list->next);print(list->value);  list!=null

return    list==null

//逆序列印

void r_print_even(node* list)

}node* list=create_list(2,5);

print_list(list);

r_print_even(list);

destroy_list(list);

/*從棧的角度分析:從2到6函式呼叫過程中,會在棧上分配空間每個指標都指向乙個數字,

到null的時候會退棧,空什麼都不會做,函式呼叫直接結束,結束就會返回,返回就會退棧

於是指向null的空間退掉,退到指向6的地方,判斷是不是偶數,偶數列印,列印完之後退棧

判讀是不是偶數,不是退棧,直到退完,列印的過程就是回溯的過程,列印的內容由棧上的

引數指標得到,在棧上做標記。回溯演算法設計時,就是利用棧來儲存資料,然後回退的時候

利用資料重新計算。*/

八皇后問題:

在乙個8*8的西洋棋棋盤上,有8個皇后,每個皇后佔一格,要求皇后間不會出現相互「攻擊」的現象(不能有兩個皇后處在同一行,同一列或同一對角線上)

關鍵資料結構定義:

棋盤:二維陣列(10*10):0表示位置為空,1表示皇后,2表示邊界。

位置:struct pos;

struct pos

;關鍵資料結構定義:方向資料

方向:水平(-1,0),(1,0)

垂直:(0,-1),(0,1)

對角線:(-1,1),(-1,-1),(1,-1),(1,1)

演算法思路:

1、初始化:j=1

2、初始化:i=1

3、從第j行開始,恢復i的有效值(通過函式呼叫棧進行回溯),判斷第i個位置

a、位置i可放入皇后:標記位置(i,j),j++轉步驟2

b、位置i不可放入皇后:i++,轉步驟a

c、當i>8時,j--,轉步驟3

結束:第8行有位置可放入皇后。

//8皇后問題

template

class queuesolution:public wobject

; struct pos :public wobject

int x;

int y;

};int m_chessboard[n][n];

pos m_direction[3];

linklist m_solution;

int m_count;

void init()

}for(int i=1;i<=size;i++)

}m_direction[0].x=-1;

m_direction[0].y=-1;

m_direction[1].x=0;

m_direction[1].y=-1;

m_direction[2].x=1;

m_direction[2].y=-1;

}void print()

cout}cout<}

cout<}

bool check(int x,int  y,int d)

while(flag);

return (m_chessboard[x][y]==2);

}void run(int j)}}

else

}public:

queuesolution()

void run()

};int main()

小結:程式執行後的棧儲存區專供函式呼叫使用,棧儲存區用於儲存實參,區域性變數、臨時變數,等,利用棧儲存區能夠方便的實現回溯演算法,八皇后問題是棧回溯的經典應用。

遞迴的思想與應用

遞迴是一種數學上分而自治的思想,將原問題分解為規模較小的問題進行處理,分解後的問題與原問題的型別完全相同,但規模較小,通過小規模問題的解,能夠輕易求得原問題的解,但是問題的分解是有限的,當邊界條件滿足時,遞迴結束直接求解否則遞迴繼續進行,在程式設計中遞迴函式就是遞迴的體現,遞迴模型的一般表示法如下 ...

六 遞迴的思想與應用

遞迴是一種數學上分而治之的思想 將原問題分解為規模較小的問題進行處理 分解後的問題與原問題的型別完全相同,但規模較小 通過小規模問題的分解,能夠輕易求得原問題的解 問題的分解是有限的 遞迴不能無限進行 當邊界條件不滿足時,分解問題 遞迴繼續進行 當邊界條件滿足時,直接求解 遞迴結束 遞迴在程式設計中...

遞迴思想的原理與應用 Recursion

函式自己呼叫自己本身 method 或methoda methodb 將乙個複雜的問題分解成幾個相同的簡單問題 遞迴需要出口 在進行迴圈時,每當一次迴圈結束,迴圈的結果作為下次迴圈的初始值,而上次迴圈占用的記憶體就會被系統釋放。因此迴圈可以沒有出口而不停無限迴圈下去。而在進行遞迴時,程式是按層次進行...