简单递归
1.递归本质(为什么可以递归)
可以递归的原因:
函数的每一次调用,都会在函数栈开辟新的内存,所以递归的本质就是栈
2.尾递归
形式:
<返回值类型> f(形参列表){
if(结束条件)
return 返回值;
/*
...
...
操作
*/
f(实参);
}
将操作放在递归调用之后另一种理解:先操作,再下一次压栈
举个例子:
void f(int x) {
if (x == 0)
return;
cout << x << endl;//这里用输出代替操作
f(x / 10);
return;
}
例如,调用f(1234):1234压栈(压栈方向,向上,所以下面的图从最后一行开始向上看)
0 | 返回条件满足; 退栈 |
1 | 输出1; f(1/10):0压栈 |
12 | 输出12; f(12/10):1压栈 |
123 | 输出123; f(123/10):12压栈 |
1234 | 输出1234; f(1234/10):123压栈 |
过程:
开始->1234压栈,输出->123压栈,输出->12压栈,输出->1压栈,输出->0压栈,0退栈->1退栈->12退栈->123退栈->1234退栈->递归结束
总结:
尾递归操作顺序按照压栈顺序访问操作。
3.头递归
形式:
<返回值类型> f(形参列表){
if(结束条件)
return 返回值;
f(实参);
/*
...
...
操作
*/
}
将操作放在递归调用之后另一种理解:先下一次压栈,再操作
举个例子:
void f(int x) {
if (x == 0)
return;
f(x / 10);
cout << x << endl;//这里用输出代替操作
return;
}
例如,调用f(1234):1234压栈(压栈方向,向上,所以下面的图从最后一行开始向上看)
0 | 返回条件满足; 退栈 |
1 | f(1/10):0压栈; |
12 | f(12/10):1压栈; |
123 | f(123/10):12压栈; |
1234 | f(1234/10):123压栈; |
过程:
开始->1234压栈->123压栈->12压栈->1压栈->0压栈,0退栈->输出1->1退栈->输出12->12退栈->输出123->123退栈->输出1234->1234退栈->递归结束
总结:
尾递归操作顺序按照退栈顺序访问操作。
4.中间递归
形式:
<返回值类型> f(形参列表){
if(结束条件)
return 返回值;
/*
...
...
操作
*/
f(实参);
/*
...
...
操作
*/
}
将操作放在递归调用之后另一种理解:操作,下一次压栈,再操作
举个例子:
void f(int x) {
if (x == 0)
return;
cout << x << endl;//这里用输出代替操作
f(x / 10);
cout << x << endl;//这里用输出代替操作
return;
}
例如,调用f(1234):1234压栈(压栈方向,向上,所以下面的图从最后一行开始向上看)
0 | 返回条件满足; 退栈 |
1 | f(1/10):0压栈; |
12 | f(12/10):1压栈; |
123 | f(123/10):12压栈; |
1234 | f(1234/10):123压栈; |
过程:
开始->1234压栈,输出->123压栈,输出->12压栈,输出->1压栈,输出->0压栈,0退栈->输出1->1退栈->输出12->12退栈->输出123->123退栈->输出1234->1234退栈->递归结束
总结:
中间操作顺序先按照压栈顺顺序操作,再按照退栈顺序访问操作。
递归总结->简单递归可以用简单循环来代替
首先,先将带操作的数使用循环入栈
尾递归
按照入栈顺序使用循环访问 还可以再入栈的时候就进行访问,这样可以少一次循环
while(i = base; i <= top; ++i){
/*
...
*/
}
头递归
按照退栈顺序使用循环访问
while(i = top; i > base ; --i){
/*
...
*/
}
中间递归
循环两次
- 相当于尾递归,按照入栈顺序访问一次(可以入栈时访问,这个循环就可省略)
-
相当于头递归,按照退栈顺序再访问一次
while(i = base; i <= top; ++i){ /* ... */ } while(i = top; i > base ; --i){ /* ...递归调用后的操作 */ }