递归:
参考:
递归是什么:在函数的定义中调用函数自身的方法。
如果调用层数比较深,需要增加额外的堆栈处理,比如参数传递需要压栈等操作,会对执行效率有一定影响
递归是用函数栈制作的,无限递归会导致栈溢出。Windows系统的Code 为0XC00000FD 见代码:StackOverFlow
// 递归:调用自己 public int get(int n) { if(n==1) //重点:一定要设置好递归出口条件,否则会无限调用自己导致堆栈溢出 { return n; } else { return get(n-1); } }
单个递归法执行顺序:
普通代码执行顺序是先把当前行代码执行完后,再继续执行下一行代码(这里不说静态类代码),递归也是一样,递归时会先把递归内的全部代码执行完后,再执行递归外的下一行代码
递归内有三行代码(每个子递归也有这三行代码):输出当前参数n、递归get(n+1)、输出当前参数n+A,递归参数依次是:1、2、3,具体执行顺序如下
1 输出当前参数1
2 递归get(1+1)
2.1 输出当前参数2
2.2 递归get(2+1)
2.2.1 输出当前参数3
2.2.2 递归get(3+1)
2.2.3 输出当前参数3+A
2.3 输出当前参数2+A
3 输出当前参数1+A
static void Main(string[] args) { // 递归法:代码执行顺序跟踪 void get(int n) { if (n <= 3) //重点:一定要设置好递归出口条件,否则会无限调用自己导致堆栈溢出 { Console.WriteLine(n); //1 输出当前参数n get(n + 1); //2 递归get(n+1) Console.WriteLine(n+"A"); //3 输出当前参数n+A } } //调用递归:执行三次分别传入1 2 3 get(1); }
输出结果:
多个递归执行顺序:二叉树的中序遍历(左节点、当前节点、右节点顺序)执行顺序说明:
递归逐级还原(类似于时光倒退,重现刚才发生的事情):
是指符合递归出口return后跳出递归,再逐还原递归出口条件外的return(知道有这回事就好,一般没用到的)
如下代码:传入一个3,输出的n是3、2、1,但是调式F10(F5是直接跳过的)跟踪时发现,在return n的时候跳出递归结束了,但是此时还会跳到return get(n-1)出进行回调2次,n变量依次变成2、3,最明显的是DateTime dt参数,还原时时间显示的就是之前调式前的时间,而不是当前时间
注意:虽然逐级还原,但是只锁定在return get(n-1)处还原,不会再执行其他代码例如:Console.WriteLine(n); //输出当前参数n sumn +=n;
static void Main(string[] args) { int sumn = 0; // 递归:调用自己 int get(int n, DateTime dt) { Console.WriteLine(n); //输出当前参数n sumn += n; if (n == 1) //重点:一定要设置好递归出口条件,否则会无限调用自己导致堆栈溢出 { return n; } else { return get(n - 1, DateTime.Now); } } get(3, DateTime.Now); Console.WriteLine("求和是:" + sumn); }
执行结果是:
循环:
在满足条件的情况下,重复执行同一段代码。使用CPU
迭代:
是重复反馈过程的活动,其目的通常是为了接近并到达所需的目标或结果。每一次对过程的重复被称为一次“迭代”,而每一次迭代得到的结果会被用来作为下一次迭代的初始值。
迭代属于循环的一种方式。
递归与循环区别:
- 递归性能低,循环性能高
- 能用循环尽量不要用递归,但是循环代码比递归难看
- 无限递归是栈溢出(是内存级别),无限循环是CPU跑满(CPU级别)
用在哪里:
- 用于方法中递归
- 斐波拉切数列
实践项目:Recursive
斐波拉切数列