递归
开发工具与关键技术: WPF
作者:罗培发
撰写时间:2021.12.28
递归
递归指的是再函数的定义中使用函数自身的方法。
语法格式的写法:
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
recursion();//调用
}
static void recursion()
{
recursion(); /* 函数调用自身 */
}
}
}
C#语言支持递归,及一个函数可以调用其自身。但在使用时,程序员需要注意定义一个冲函数退出的条件,负责会进入死循环。
递归函数再结局许多数学问题上起了至关重要的作用,比如计算过一个数的阶乘、生成斐波那契数列,等等。
常见使用场景:
1、 阶乘
2、 斐波那契数列
3、 汉诺塔
4、 遍历硬盘文件
数的阶乘
本文将未大家带来时用C#递归算法来计算阶乘的方法,通过一个简单实例来说明,参考。
一般来说,想要实现一个阶乘,比如6*5*4*3*2*1这个简单阶乘,一般会首先想到使用循环遍历,如下面代码所示:
Class Program
{
static void Main(string[] args)
{
Console.WriteLine("请输入一个数");
int number = Convert.ToInt32(Console.ReadLine());
double result = JieCheng(number);
Console.WriteLine(number.ToString() + "的阶乘结果是:" + result.ToString()); Console.ReadKey();
}
public static double JieCheng(int number)
{
//终止条件
if (number == 0)
{
return 0; }
//初始值必须设置为1
double result = 1;
//循环
for (int i = number; i >= 1; i--)
{
//递归
result = result*i;
}
return result;
}
但是这种阶乘其实还有另一种实现方式:6*(6-1)*(6-2)*(6-3)*(6-4)*(6-5)或
6*(6-1)*(5- 1)*(4-1)*(3-1)*(2-1),也就是说后面数总是有前面的数减1得到的。
当实现的逻辑相同,且内部递归方法的参数可以右外部递归方法的参数,经过某种算放而获得,这正是递归哦登场的时候,递归方式实现代码如下:
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
//初始化变量
int i = 6;
//控制台输出
Console.WriteLine("{0} 的阶乘为 {1}", i, factorial(i));
//控制台读取
Console.Read();
}
public static double factorial(int i)
{
//数值小于1则返回1
if (i <= 1)
{
return 1;
}
//阶乘
return i * factorial(i - 1);
}
}
}
斐波那契数列
斐波纳契数列,又称黄金分割数列,指的是这样一个数列:斐波那契数列f(n)=f(n1)+f(n-2) =1、1、2、3、5、8、13、21、34…
这个数列从第三项开始,每一项都等于前两项之和。
下面的实例使用递归函数生成一个给定的数的斐波那契数列:
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
int i;
for (i = 0; i < 10; i++)
{
//控制台输出
Console.WriteLine("{0}\t\n", fibonaci(i));
}
//控制台读取
Console.Read();
}
public static int fibonaci(int i)
{
if (i == 0)
{
return 0;
}
if (i == 1)
{
return 1;
}
//斐波那契数列
return fibonaci(i - 1) + fibonaci(i - 2);
}
}
}
总结
递归就是指函数直接或间接的调用自己,递归是基于栈来实现的。递归的经典列子就是斐波那契数列。一般如果能用递归来实现的程序,那它也能用来循环来实现。用递归来实现的花,diamagnetic看起来更清晰一些,但递归的性能并不占优势,时间复杂甚至也会更大一些。
(1)要实现递归,必须满足2个条件:
1、可调节用自己
就是我们要解决的这个问题,可以通过函数调用自己的方式来解决,既可以通过将大问题
分解为子问题,然后子问题再可以分解子子问题,这样不停的分解。并且大问题与子问题/
子子问题的解决西路是完全一样的,只不过数据不一样。因此这些问题都是通过某一个函
数去解决的,最终我们看到的就是不停的函数调用自己,然后把问题化解了。
如果这个问题不能分解为子问题,或子问题的解决方法与大问题不一样,那就无法通过递
归调用来解决。
2、可停止调用自己
停止调用的条件非常关键,就是大问题不停的一层层分解为小问题后,最终必须有 一个条件是来终止这种分解动作的(也就是停止调用自己),做递归运算一定要有 这个终止条件,否则就会陷入无限循环。
(2)我们写递归代码最重要的就是写2点:
1. 递推公式
这也是函数自己调用自己的关键点。因此我们在写递归代码的时候最首先要做的就 是思考整个逻辑中的递推公式。
2. 递归停止条件
这就是递归的出口,想出了递推公式之后,就要考虑递归停止条件是啥,没有停止 条件就会无限循环了,通常递归的停止条件是程序的边界值。 我们对比6阶乘,可以看出递归的方式比循环的方式在程序结构上更简洁清晰,代码 也更易读。但递归调用的过程中会建立函数副本,创建大量的调用栈,如果递归的 数据量很大,调用层次很多,就会导致消耗大量的时间和空间,不仅性能较低,甚 至会出现堆栈溢出的情况。
我们在写递归的时候,一定要注意递归深度的问题,随时做好判断,防止出现堆栈 溢出。
另外,我们在思考递归逻辑的时候,没必要在大脑中将整个递推逻辑一层层的想透 彻,一般人都会绕晕的。大脑很辛苦的,我们应该对它好一点。我们只需要关注当 前这一层是否成立即可,至于下一层不用去关注,当前这一层逻辑成立了,下一层 肯定也会成立的,最后只需要拿张纸和笔,模拟一些简单数据代入到公式中去校验 一下递推公式对不对即可。