数据结构与算法之美(递归)

一、什么是递归?

说白了 就是自己调用自己 大家应该看过那个 盒子套盒子视频把
常用的 有 dfs啦 还有二叉树(链表形式)遍历

满足递归的条件:

  1. 一个问题的解可以分解为几个子问题的解,这个问题与分解之后的子问题,除了数据规模不同,求解思路完全一样(越来越简单)
  2. 存在递归终止条件(换句话说 就是出口)

模板:

void recursion(参数0) {
    
    
    if (终止条件) {
    
    
        return;
    }
    
    recursion(参数1);
}

二、为什么使用递归?递归的优缺点?

  • 1.优点:代码的表达力很强,写起来简洁。
  • 2.缺点:空间复杂度高、有堆栈溢出风险、存在重复计算、过多的函数调用会耗时较多等问题。

三、什么样的问题可以用递归解决呢?

1,阶乘

//递归法就n的阶乘
public class MainClass {
    
    
    public static void main(String args[]) {
    
    
    for (int counter = 0; counter <= 10; counter++){
    
    
        System.out.printf("%d! = %d\n", counter,
        factorial(counter));
    }
    }
    public static long factorial(long number) {
    
    
        if (number <= 1)
            return 1;
        else
            return number * factorial(number - 1);
    }
}

2,斐波那契数列

我们再来看另一道经典的递归题,就是斐波那契数列,数列的前几项如下所示

[1,1,2,3,5,8,13……]

递归的两个条件,一个是终止条件,我们找到了。还一个是调用自己,我们知道斐波那契数列当前的值是前两个值的和,也就是

fibonacci(n) =fibonacci(n - 1) + fibonacci(n - 2)

//1,1,2,3,5,8,13……
public int fibonacci(int n) {
    
    
    if (n == 1 || n == 2)
        return 1;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

3,汉诺塔

相信大家都看过这个吧
在这里插入图片描述

汉诺塔的原理这里再简单提一下,就是有3根柱子A,B,C。A柱子上由上至下依次由小至大排列的圆盘。把A柱子上的圆盘借B柱子全部移动到C柱子上,并且移动的过程始终是小的圆盘在上,大的在下。我们还是用递归的方式来解这道题。

#include <bits/stdc++.h>
 
using namespace std;
 
void Move(int n, char A, char B, char C)
{
    
    
	if (n == 1)
	{
    
    
		//圆盘只有一个时,只需将其从A塔移到C塔
		cout << "把" << n << " 从" << A << " 移动到" << C << endl;
	}
	else
	{
    
    
		Move(n - 1, A, C, B);//递归,把A塔上编号1~n-1的圆盘移到B上,以C为辅助塔
		cout << "把" << n << " 从" << A << " 移动到" << C << endl;//把A塔上编号为n的圆盘移到C上
		Move(n - 1, B, A, C);//递归,把B塔上编号1~n-1的圆盘移到C上,以A为辅助塔
	}
}
 
void Hanoi(int n)
{
    
    
	if (n <= 0)
		return;
	Move(n, 'A', 'B', 'C');
}
 
int main()
{
    
    
	Hanoi(3);
	return 0;
}

四、如何实现递归?

到这里再说 怎么实现 相信大家都看了上面的递归代码了 也明白的差不多了

总的一句 :

写出递推公式,找到终止条件

五、递归常见问题及解决方案

  • 警惕堆栈溢出:可以声明一个全局变量来控制递归的深度,从而避免堆栈溢出。
    解决方法:
    可以通过在代码中限制递归调用的最大深度的方式来解决这个问题
  • 警惕重复计算:通过某种数据结构来保存已经求解过的值,从而避免重复计算。
    解决方法:
    为了避免重复计算,我们可以通过一个数据结构(比如散列表)来保存已经求解过的f(k)。当递归调用到f(k)时,先看下是否已经求解过了。如果是,则直接从散列表中取值返回,不需要重复计算,这样就能避免刚讲的问题了。

六、怎么将递归代码改写为非递归代码?

这个很简单 将递归代码改成循环(也就是迭代)即可 具体怎么写
还是用斐波拉契来说

递归代码:

int fib(int n){
    
      
         if(n>1) return fib(n-1) + fib(n-2);  
         else return n; // n = 0, 1时给出recursion终止条件  
    }  

迭代:

int fib(int n){
    
      
    int i, temp0, temp1, temp2;        
    if(n<=1) return n;  
    temp1 = 0;  
    temp2 = 1;  
    for(i = 2; i <= n; i++){
    
      
        temp0 = temp1 + temp2;  
        temp2 = temp1;  
        temp1 = temp0;  
    }  
    return temp0;  
} 

猜你喜欢

转载自blog.csdn.net/qq_54729417/article/details/122884762
今日推荐