栈与递归

  递归的概念html

  栈的一个典型应用是程序设计中的递归过程的设计与实现,用栈来保存调用过程当中的参数和返回地址。ios

递归过程(或函数)就是子程序或函数中直接或间接的调用本身。算法

  递归定义实例:编程

  1.某人祖先的递归定义:数组

某人的双亲是他的祖先[基本状况]函数

某人祖先的双亲一样是某人的祖先[递归步骤]性能

  2.阶乘函数spa

       

   3.斐波那契数列设计

 

 

   通常递归分为直接递归间接递归code

 

                     

 

 

 

 

  4.Ackerman函数

  当两个连续函数都趋近于无穷时,咱们经常使用洛必达法则来比较它们趋向无穷的快慢。函数的阶越高,

它趋向无穷的速度就越快。定义在正整数域上的函数中,n!趋向于正无穷的速度很是快,因此在算法设计中

若是出现这样的时间复杂度就很是糟糕。logn趋向无穷的速度则很是慢。

   而Ackerman函数能够比n!的增加速度快得多,比logn的增加速度慢的多。同时,并非全部的

递归函数都有通项公式,Ackerman函数就是一个例子,它是双向递归函数,有两个自变量(独立),定义以下:

  

              Ack(0,n)=n+1 , n>=0;

             

              Ack(m,0)=Ack(m-1,1) , m>0;

             

              Ack(m,n)=Ack(Ack(m-1,n),n-1) , n,m>0

 1 #include <stdio.h>
 2 
 3 #include <stdlib.h>
 4 
 5 int   ack(int  m, int n)  6 
 7 {  8 
 9     int z; 10 
11     if (m == 0) 12 
13          z = n + 1; //出口
14 
15     else if (n == 0) 16 
17          z = ack(m - 1, 1);  //形参m降阶
18 
19     else
20 
21          z = ack(m - 1, ack(m, n - 1)); //对形参m,n降阶
22 
23     return z; 24 
25 } 26 
27         
28 
29 void main() { 30 
31      int m, n; 32 
33     scanf("%d%d", &m, &n); 34 
35     printf("Ack(%d,%d)=%d\n",m,n, ack(m, n)); 36 
37  
38 
39 }

 

 

  递归过程的内部实现

  从递归调用的过程可知,它们恰好符合后进先出的原则。所以,利用栈实现递归过程再合适不过。

下面以求n!为例,介绍它的递归实现过程。

  递归算法为

  

 1 int fact(int N)  2 
 3 {  4 
 5 int result;  6 
 7 if(N == 0)  8 
 9 result = 1; 10 
11 else
12 
13 result = fact(n-1)*n  //递归调用自身
14 
15 return result; 16 
17 }

 

   在递归执行过程当中,须要一个栈LS保存递归调用前的参数及递归的返回地址。参数为fact中的现行值,返回地址为递归语句的下一语句入口地址。设第1次调用的返回地址为p1,第2次调用的返回地址为p2, … 如图1所示

 具体实现为

(1)遇递归调用时,将当前参数与返回地址保存在LS中;

(2)遇递归返回语句时,将LS栈顶的参数及返回地址一并弹出,按当前返回地址继续执行

 

 

  分析递归程序的运行机制,递归用须要1-7的过程,而非递归只须要4-7过程,可见递归程序的运行效率并不高,

即并不能节约运行时间,相反,因为保存大量的递归调用的参数和返回地址,还要消耗必定的时间,再加上空间上的消耗,

同非递归递归函数相比毫无优点而言。可是尽管递归在时空方面不占优点,但有编程方便,结构清楚,逻辑结构严谨等优点。

递归的致命缺点是时空性能很差,可是咱们能够用非递归方法来解决递归问题,从而改善程序的时空性能。

递归消除

       递归的消除有两种:简单递归消除和基于栈的递归消除

  1.简单递归消除

  尾递归是指递归调用用语句只有一个, 且处于算法的最后,尾递归是单向递归的特例. n!递归算法是尾递归的一个典型例子.

  对于尾递归,当递归返回时, 返回到上一层递归调用语句的下一语句时已到程序的末尾, 因此不须要栈LS保存返回地址。

  2.基于栈的递归消除

  具体方法:将递归算法中的递归调用语句改为压入操做;将递归返回语句改成弹出操做。

sqstack.h:

https://www.cnblogs.com/mwq1024/p/10146943.html

//计算n!不须要保存返回地址 缘由上面说了

 1 #include <stdio.h>
 2 
 3 #include <stdlib.h>
 4 
 5 #include "sqstack.h" //引入顺序栈储存结构及其基本操做
 6 
 7  
 8 
 9 int fact(int N) { 10 
11     int result = 1; 12 
13     SqStack s,*S; 14 
15     S = &s; 16 
17     StackType *e; 18 
19     e = (StackType*)malloc(sizeof(StackType)); 20 
21  InitStack(S); 22 
23     while (N) 24 
25  { 26 
27  Push(S, N); 28 
29          N--; 30 
31  } 32 
33     while (!EmptyStack(S)) 34 
35  { 36 
37          result *= *(S->top - 1); 38 
39  Pop(S, e); 40 
41  } 42 
43     return result; 44 
45    
46 
47 } 48 
49 void main() 50 
51 { 52 
53     int n; 54 
55     printf("请输入n的阶数:"); 56 
57     scanf("%d", &n); 58 
59     printf("result is %d\n", fact(n)); 60 
61 }

 

    利用栈解决汉诺塔问题

    把三个塔座当作三个栈,入栈和出栈就至关于移动盘子。

汉诺塔算法的非递归实现C++源代码

  

 1 #include <iostream>
 2 
 3 using namespace std;  4 
 5 //圆盘的个数最多为64
 6 
 7 const int MAX = 64;  8 
 9 //用来表示每根柱子的信息
 10 
 11 struct st{  12 
 13 int s[MAX]; //柱子上的圆盘存储状况
 14 
 15 int top; //栈顶,用来最上面的圆盘
 16 
 17 char name; //柱子的名字,能够是A,B,C中的一个
 18 
 19  
 20 
 21 int Top()//取栈顶元素
 22 
 23 {  24 
 25 return s[top];  26 
 27 }  28 
 29 int Pop()//出栈
 30 
 31 {  32 
 33 return s[top--];  34 
 35 }  36 
 37 void Push(int x)//入栈
 38 
 39 {  40 
 41 s[++top] = x;  42 
 43 }  44 
 45 } ;  46 
 47 long Pow(int x, int y); //计算x^y
 48 
 49 void Creat(st ta[], int n); //给结构数组设置初值
 50 
 51 void Hannuota(st ta[], long max); //移动汉诺塔的主要函数
 52 
 53 int main(void)  54 
 55 {  56 
 57 int n;  58 
 59 cin >> n; //输入圆盘的个数
 60 
 61 st ta[3]; //三根柱子的信息用结构数组存储
 62 
 63 Creat(ta, n); //给结构数组设置初值
 64 
 65 long max = Pow(2, n) - 1;//动的次数应等于2^n - 1
 66 
 67 Hannuota(ta, max);//移动汉诺塔的主要函数
 68 
 69 system("pause");  70 
 71 return 0;  72 
 73 }  74 
 75 void Creat(st ta[], int n)  76 
 77 {  78 
 79 ta[0].name = 'A';  80 
 81 ta[0].top = n-1;  82 
 83 //把全部的圆盘按从大到小的顺序放在柱子A上
 84 
 85 for (int i=0; i<n; i++)  86 
 87 ta[0].s[i] = n - i;  88 
 89 //柱子B,C上开始没有没有圆盘
 90 
 91 ta[1].top = ta[2].top = 0;  92 
 93 for (int i=0; i<n; i++)  94 
 95 ta[1].s[i] = ta[2].s[i] = 0;  96 
 97 //若n为偶数,按顺时针方向依次摆放 A B C
 98 
 99 if (n%2 == 0) 100 
101 { 102 
103 ta[1].name = 'B'; 104 
105 ta[2].name = 'C'; 106 
107 } 108 
109 else //若n为奇数,按顺时针方向依次摆放 A C B
110 
111 { 112 
113 ta[1].name = 'C'; 114 
115 ta[2].name = 'B'; 116 
117 } 118 
119 } 120 
121 long Pow(int x, int y) 122 
123 { 124 
125 long sum = 1; 126 
127 for (int i=0; i<y; i++) 128 
129 sum *= x; 130 
131 return sum; 132 
133 } 134 
135 void Hannuota(st ta[], long max) 136 
137 { 138 
139 int k = 0; //累计移动的次数
140 
141 int i = 0; 142 
143 int ch; 144 
145 while (k < max) 146 
147 { 148 
149 //按顺时针方向把圆盘1从如今的柱子移动到下一根柱子
150 
151 ch = ta[i%3].Pop(); 152 
153 ta[(i+1)%3].Push(ch); 154 
155 cout << ++k << ": " <<
156 
157 "Move disk " << ch << " from " << ta[i%3].name <<
158 
159 " to " << ta[(i+1)%3].name << endl; 160 
161 i++; 162 
163 //把另外两根柱子上能够移动的圆盘移动到新的柱子上
164 
165 if (k < max) 166 
167 { //把非空柱子上的圆盘移动到空柱子上,当两根柱子都为空时,移动较小的圆盘
168 
169 if (ta[(i+1)%3].Top() == 0 ||
170 
171 ta[(i-1)%3].Top() > 0 &&
172 
173 ta[(i+1)%3].Top() > ta[(i-1)%3].Top()) 174 
175 { 176 
177 ch = ta[(i-1)%3].Pop(); 178 
179 ta[(i+1)%3].Push(ch); 180 
181 cout << ++k << ": " << "Move disk "
182 
183 << ch << " from " << ta[(i-1)%3].name 184 
185 << " to " << ta[(i+1)%3].name << endl; 186 
187 } 188 
189 else
190 
191 { 192 
193 ch = ta[(i+1)%3].Pop(); 194 
195 ta[(i-1)%3].Push(ch); 196 
197 cout << ++k << ": " << "Move disk "
198 
199 << ch << " from " << ta[(i+1)%3].name 200 
201 << " to " << ta[(i-1)%3].name << endl; 202 
203  } 204 
205  } 206 
207  } 208 
209 }
相关文章
相关标签/搜索