如何解决递归中的重复计算(重叠子问题)

本文探讨了递归算法中的重叠子问题,通过斐波那契数列和正则表达式匹配的例子,展示了暴力递归的效率问题。通过引入备忘录和哈希表等数据结构,将时间复杂度从O(2^n)优化到O(n),避免了重复计算,提高了递归算法的效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

递归是一种自顶向下的、很直观的编程方法,解释性强。但在写递归的时候,最关键的问题是怎样避免递归过程中可能产生的大量重复计算,即重叠子问题,否则,写出来的代码即便没有逻辑错误,也几乎会因为超时无法运行。所以说,一旦选择了递归,基本上都要连同做后续的优化工作,这也是本文主要探讨的话题。

由于递归代码是很容易写的,这篇文章要做的是在写好暴力递归之后,怎样进行优化,以避免重复计算。下面通过几个简单的例子说明(题目来自力扣):

提示:不要关注题目内容,直接对比暴力递归代码和优化后代码,很容易找到优化套路,就在暴力基础上改个两三处就可以了。

1. 斐波那契数列

写一个函数,输入 n,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1 F(N) = F(N - 1) + F(N - 2), 其中 N > 1.

1.1 暴力递归
int fib(int n){
   
   
	if(n < 2) return n;
	return fib(n - 1) + fib(n - 2);
}

暴力递归的代码如此简洁,甚至稍微整理一下一行足矣。但这样写存在严重的效率问题,当 n 稍大时,基本就无法运行了,因为其时间复杂度达到了 O ( 2 n ) O(2^n) O(2n) 之大,具体这个时间复杂度怎么求得画个图就明白了:
如下图所示,时间复杂度 = 子问题的个数 * 子问题的时间复杂度。由图示可知,子问题的个数为 2 n 2^n 2n,每个子问题只做了一次加法操作,故算法总的时间复杂度为 O ( 2 n ) O(2^n) O(2n)
在这里插入图片描述
从图中也可以看出,其中包含了大量的重复计算,下面,我们通过引入备忘录的方法解决该重复计算问题,即将算过的子问题存起来,以使每个子问题只计算一遍。

1.2 优化递归
int recur(vector<int>& memo, int n){
   
   
	if(n < 2) return n;
	if(memo[n] != 0) return memo[n];
	memo[n] = recur(memo, n - 1) + recur(memo, n - 2);
	return memo[n];
}

int fib(int n){
   
   
	if(n < 2) return n;
	vector<int> memo(n + 1, 0);
	int res = recur(memo, n);
	return res;
}

通过memo数组,将计算过的子问题存储在其中,再遇到时,直接反回即可。这样,时间复杂度变为 O ( n ) O(n) O(n),相当于只计算了最左侧一条线,其余都都是直接从数组返回的。
在这里插入图片描述

2. 正则表达式匹配

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。其中:
‘.’ 匹配任意单个字符;
‘*’ 匹配零个或多个前面的那一个元素。

所谓匹配,是要涵盖整个字符串 s的,而不是部分字符串。
例1
输入:s = “aa” p = “a*”
输出:true
解释:因为 ‘’ 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 ‘a’。因此,字符串 “aa” 可被视为 ‘a’ 重复了一次。
例2
输入:s = “aab” p = “c*a*b”
输出:true
解释:因为 '
’ 表示零个或多个,这里 ‘c’ 为 0 个, ‘a’ 被重复一次。因此可以匹配字符串 “aab”。

首先,我们来整理一下解题思路:

  1. 如果不考虑 ‘*’ ,那就很容易了,只需要将待匹配字符串 s 和模式字符串 p 从第一个字符开始向后一一比对即可,其中, p 中的 ‘.’ 可以匹配 s 中的任意字符,此时可以写出如下代码:
bool isMatch
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值