数据结构教程读书笔记_递归
遞歸的定義
在定義一個過程或函數時,出現直接或者間接調用自己的成分,稱之為遞歸。
若直接調用自己,稱之為直接遞歸;若間接調用自己,稱之為間接遞歸。
如果一個遞歸函數中遞歸調用語句是最后一條執行語句,則稱這種遞歸調用為尾遞歸。
尾遞歸算法:可以用循環語句轉換為等價的非遞歸算法
其他遞歸算法:可以通過棧來轉換為等價的非遞歸算法
使用遞歸的場景
(1)定義是遞歸的
有許多數學公式、數列等的定義是遞歸的。例如,求n!和Fibonacci數列等。這些問題的求解過程可以將其遞歸定義直接轉化為對應的遞歸算法。
(2)數據結構是遞歸的
有些數據結構是遞歸的。 例如, 單鏈表就是一種遞歸數據結構, 其節點類型定義如下:
1 typedef struct LNode
2 {
3 ElemType data;
4 struct LNode *next; //指向同類型節點的指針
5 } LinkList;
不帶頭節點單鏈表示意圖:
(3)問題的求解方法是遞歸的
遞歸模型
一個遞歸模型是由遞歸出口和遞歸體兩部分組成。
遞歸出口確定遞歸到何時結束;遞歸體確定遞歸求解時的遞推關系。
f(sn) =g(f(si), f(si+1), …, f(sn-1), cj, cj+1, …, cm)
其中g是一個非遞歸函數,c是常量。
把一個不能或不好直接求解的“大問題”轉化成一個或幾個“小問題”來解決; 再把這些“小問題”進一步分解成更小的“小問題” 來解決。 直到每個“小問題”都可以直接解決(此時分解到遞歸出口)。
但遞歸分解不是隨意的分解,遞歸分解要保證“大問題”與“小問題”相似,即求解過程與環境都相似。
f(s1) =m1
f(sn) =g(f(sn-1), cn-1)
求f(sn)的分解過程如下:
遇到遞歸出口發生“質變” , 即原遞歸問題便轉化成可以直接求解的問題。 求值過程:
這樣f(sn)便計算出來了,因此遞歸的執行過程由分解和求值兩部分構成。
求遞歸模型的步驟
(1)對原問題f(s)進行分析, 稱為“ 大問題” ,假設出合理的“ 小問題” f(s’)
(2)假設f(s’)是可解的, 在此基礎上確定f(s)的解, 即給出f(s)與f(s’)之間的關系,即遞歸體
(3)確定一個特定情況(如f(1)或f(0))的解,即遞歸出口
應用
1、采用遞歸算法求實數數組A[0..n-1]中的最小值
分析:
假設f(A,i-1)已求出, 則f(A,i)=MIN(f(A,i-1), A[i]), 其中MIN()為求兩個值較小值函數。
當i=0時,只有一個元素,有f(A,i)=A[0]。
遞歸模型:
f(A,i) =A[0] 當i=0時
f(A,i) = MIN(f(A,i-1), A[i]) 其他情況
代碼:
1 float f(float A[], int i)
2 {
3 float m;
4 if (i == 0)
5 return A[0];
6 else
7 {
8 m = f(A, i - 1);
9
10 if (m > A[i])
11 return A[i];
12 else
13 return m;
14 }
15 }
2、求單鏈表中數據節點個數
分析:
空單鏈表的數據節點個數為0,即f(L)=0 當L=NULL
對于非空單鏈表:
考慮一下我們的鏈表模型:
則可以得出下面的圖:
遞歸模型:
f(L) = 0 當L=NULL
f(L) =f(L->next) + 1 其他情況
代碼:
1 int count(Node *L)
2 {
3 if (L == NULL)
4 return 0;
5 else
6 return count(L->next) + 1;
7 }
3、正向顯示所有節點值
分析:
假設f(L->next)已求解
f(L)等價于輸出L->data; f(L->next);
遞歸模型:
f(L) 不做任何事件 當L=NULL
f(L) 輸出L->data;f(L->next) 其他情況
代碼:
1 void traverse(Node *L)
2 {
3 if (L == NULL)
4 return;
5
6 printf("%d ", L->data);
7 traverse(L->next);
8 }
4、反向顯示所有節點值
分析:
假設f(L->next)已求解
f(L)等價于f(L->next);輸出L->data;
遞歸模型:
f(L) 不做任何事件 當L=NULL
f(L) f(L->next);輸出L->data 其他情況
代碼:
1 void traverseR(Node *L)
2 {
3 if (L == NULL)
4 return;
5
6 traverseR(L->next);
7 printf("%d ", L->data);
8 }
5、采用遞歸算法求解迷宮問題,并輸出從入口到出口的所有迷宮路徑
分析:
遞歸模型:
mgpath(xi,yi,xe,ye,path) 將(xi,yi)添加到path中;輸出path中的迷宮路徑; 若(xi,yi)=(xe,ye)
mgpath(xi,yi,xe,ye,path) 對于(xi,yi)四周的每一個相鄰方塊(i,j):
(1)將(xi,yi)添加到path中;
(2)置mg[xi][yi]=-1;
(3)mgpath(i,j,xe,ye,path);
(4)path回退一步并置mg[xi][yi]=0; (在一個“小問題” 執行完后回退是為了找所有解)
若(xi,yi)不為出口且可走
代碼:
1 typedef struct
2 {
3 int i; //當前方塊的行號
4 int j; //當前方塊的列號
5 } Box;
6
7 typedef struct
8 {
9 Box data[MaxSize];
10 int length; //路徑長度
11 } PathType; //定義路徑類型
12
13 int mg[M+2][N+2] = //M=4, N=4
14 {
15 {1, 1, 1, 1, 1, 1},
16 {1, 0, 0, 0, 1, 1},
17 {1, 0, 1, 0, 0, 1},
18 {1, 0, 0, 0, 1 ,1},
19 {1, 1, 0, 0, 0, 1},
20 {1, 1, 1, 1, 1, 1}
21 };
22
23 void mgpath(int xi,int yi,int xe,int ye,PathType path)
24 //求解路徑為:(xi,yi) ? (xe,ye)
25 {
26 int di,k,i,j;
27 if (xi == xe && yi == ye)
28 {
29 path.data[path.length].i = xi;
30 path.data[path.length].j = yi;
31 path.length++;
32 printf("迷宮路徑%d如下:
",++count);
33
34 for (k = 0; k < path.length; k++)
35 {
36 printf(" (%d, %d)", path.data[k].i, path.data[k].j);
37
38 if ((k + 1) % 5 == 0) //每輸出每5個方塊后換一行
39 printf("
");
40 }
41 printf("
");
42 }
43 else //(xi,yi)不是出口
44 {
45 if (mg[xi][yi] == 0) //(xi,yi)是一個可走方塊
46 {
47 di= 0;
48 while (di<4) //對于(xi,yi)四周的每一個相鄰方位di
49 {
50 switch(di) //找方位di對應的方塊(i,j)
51 {
52 case 0:
53 i = xi-1; j = yi; break;
54 case 1:
55 i = xi; j = yi + 1; break;
56 case 2:
57 i = xi + 1; j = yi; break;
58 case 3:
59 i = xi; j = yi - 1; break;
60 }
61 path.data[path.length].i = xi;
62 path.data[path.length].j = yi;
63 path.length++; //路徑長度增1
64 mg[xi][yi]=-1; //避免來回重復找路徑
65
66 mgpath(i,j,xe,ye,path);
67
68 path.length--; //回退一個方塊
69 mg[xi][yi]=0; //恢復(xi,yi)為可走
70 di++;
71 } //-while
72 } //- if (mg[xi][yi]==0)
73 } //-遞歸體
74 }
75
76 int main()
77 {
78 PathType path;
79 path.length=0;
80
81 mgpath(1, 1, 4, 4, path);
82
83 return 0;
84 }
初始:
結果:
本文圖片參考自:配套ppt課件
總結
以上是生活随笔為你收集整理的数据结构教程读书笔记_递归的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Windows Shell 编程 第六章
- 下一篇: HBuilderX下载和安装