递归遍历与for循环遍历:递归遍历实现、理解简单

1、想法来源:力扣题14- I. 剪绳子
给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m-1] 。请问 k[0] x k[1] x ... x k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

示例 1:
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1

示例 2:
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

2、思路:
我们做题多了一般会想到动态规划解法,自底向上扩展绳子长度,完成题解。但是初学者可能只会想到暴力搜索,遍历所有绳子剪法,进行比较得到结果
对于遍历通常想到for循环遍历,但对于此题,每层for循环表示什么、其范围取值是多少都较难以想到,因此for循环解法失败。但递归遍历方法则实现、理解较为简单,应谨记,在解题时较为常用
思路来源:作者:z1m,来源:力扣
方法一:暴力递归遍历
我们往往会在头脑中形成一种很直观的暴力解法,就是列举出所有的情况,找到乘积最大的那个解。
设 F(n)为长度为n的绳子可以得到的最大乘积,对于每一个F(n),可以得到如下分解:
在这里插入图片描述
从上图看出我们可以把求解F(n) 的问题分解成求解 F(n-1)的问题,以此类推,直到求解到 F(2)时,F(2) = 1,递推回去,问题就得到了解决。这用到的就是分治的思想。
分治思想的解决方法往往是递归,注意到我们每次将一段绳子剪成两段时,剩下的部分可以继续剪,也可以不剪, 因此我们得到了递归函数 F(n)=max(i*(n-i),i*F(n-i)), i=1,2,...,n-2
代码:

class Solution {
    
    
public:
    int cuttingRope(int n) {
    
    
    	if(n == 2) return 1;
    	int res = -1;
    	for(int i = 1;i<n;i++){
    
    
			res = max(res, max(i * cuttingRope(n-i),i*(n-1)) );
		}
		return res;
    }
};

方法二:记忆化技术(自顶向下)
上述暴力解法会超时,但是很多进阶解法往往是暴力解法的优化。注意到上述代码中超时的原因主要是因为重复计算了 F(n),为了避免重复计算可以使用 记忆化(memoization)技术。
记忆化技术的代码使用数组 f 来保存长度为 i时的最大长度 f[i],最后返回 f[n]即可。
代码:

class Solution {
    
    
public:
    int cuttingRope(int n) {
    
    
    	if(n == 2) return 1;
    	if (f[n] != 0) // 如果f[n]已经计算过,直接返回避免重复计算
            return f[n];
    	int res = -1;
    	for(int i = 1;i<n;i++){
    
    
			res = max(res, max(i * cuttingRope(n-i),i*(n-1)) );
		}
		f[n] = res;
		return res;
    }
};

记忆化搜索也叫“备忘录法”,它从类似上边树形图结构中的 F(n)出发,逐步递归到已知值 F(2),可以理解成为自顶向上的解决办法

动态规划解法见原博客:作者:z1m,来源:力扣

总结:

1、递归遍历实现、理解简单,比for循环好用,但时间复杂度稍高(常系数部分大)。
2、对于递归遍历通常重复计算子问题的解,因此常利用记忆法,保存子问题的解。

猜你喜欢

转载自blog.csdn.net/qq_33726635/article/details/106616700
今日推荐