The best time to plant a tree was 10 years ago. The second best time is now
翻译:种一棵树最好的时间是十年前,其次是现在
所谓运气,不过是机会碰巧遇到了你的努力
本章内容是对递归详细讲解,中间穿插了较多的递归经典案例,方便我们理解递归的思想以及使用递归去解决实际的问题。
1、函数递归
什么是递归:程序调用自身的编程技巧称为递归(recursion)。递归做为一种算法在程序设计语言中广泛应用。一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
递归的主要思考方式在于 : 把大事化小
注意:函数在调用的时候会向内存申请空间。(递归的过程就是函数的不停调用过程)
最简单的递归函数:主函数调用主函数
#include<stdio.h>
int main()
{
printf("hello world\n");
main();
return 0;
}
上述代码结果:死循环,栈溢出(stack overflow)
为什么会出现栈溢出呢?
要想搞懂栈溢出的原因,首先我们要明白程序运行过程中内存的划分分区
每一次函数的调用,都需要在栈区分配一定的空间(也就是说函数调用也在栈区开辟空间),调用次数太多,栈空间不够分配(被耗干),导致栈溢出。
练习1 : (画图讲解)
接受一个整型值(无符号),按照顺序打印它的每一位。例如︰输入∶1234,输出1234.
要顺序打印它的每一位,就需要得到它的每一位,1234最容易得到的就是个位
1234 % 10 = 4
1234 / 10 = 123 % 10 = 3
123 / 10 = 12 % 10 = 2
12 / 10 = 1 % 10 = 1
1 / 10 = 0
#include<stdio.h>
void print(int n)
{
if (n > 9)
{
print(n / 10);
}
printf("%d ", n % 10);
}
int main()
{
unsigned int num = 0;
scanf("%d", &num);//1234
print(num);//打印1 2 3 4
//print(1234)
//print(123) 4
//print(12) 3 4
//print(1) 2 3 4
// 1 2 3 4
//当()里面的数字大于9的时候就拆分,小于9,为个位数的时候停止拆分,进行打印
return 0;
}
画图详解:
比较上面两个递归函数,我们可以看到: 递归的两个必要条件
①存在限制条件,当满足这个限制条件的时候,递归便不再继续。
②每次递归调用之后越来越接近这个限制条件。
注意:这两个条件是必要条件,不是充分条件,也就是说递归函数一定满足这两个条件,但是满足这两个条件不一定是递归。看下面这个例子:
按F10进行调试:
每一次函数的调用,都需要在栈区分配一定的空间,调用次数太多,栈空间不够分配(被耗干),导致栈溢出。
所以我们在写递归代码的时候,一定要注意以下几点:
1、不能死递归,要有跳出条件,每次递归逼近跳出条件
2、递归层层不能太深
练习2∶(画图讲解)
编写函数不允许创建临时变量,求字符串的长度。
初步解题思路:
#include<stdio.h>
#include<