遞迴是一種數學上分而自治的思想
將原問題分解為規模較小的問題進行處理。
分解後的問題與原問題的型別完全相同,但規模較小。通過小規模問題的求解,能夠輕易求得原問題的解。
問題的分解是有限的(遞迴不能無限進行):當邊界條件不滿足時,分解問題(遞迴繼續進行),當邊界條件滿足時,直接求解(遞迴結束)。
地規模型的一般表示法:
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 將乙個複雜的問題分解成幾個相同的簡單問題 遞迴需要出口 在進行迴圈時,每當一次迴圈結束,迴圈的結果作為下次迴圈的初始值,而上次迴圈占用的記憶體就會被系統釋放。因此迴圈可以沒有出口而不停無限迴圈下去。而在進行遞迴時,程式是按層次進行...