递推与递归

在刷题的过程中刷到了两道既能用递归来做,也能用递推来做的题目,但是每道题目都有时间和空间的限制,递推和递归并无明显的优劣,要结合题目要求具体分析,有时候用递推更快,无法用递归;有时用递归更快,无法用递推。

题目1:扩充序列

给定一个只包含一个数字 1 的序列 [1]。

现在,要对该序列进行 n−1 次扩充。

每次扩充时,首先要将序列本身添加到其自身的尾部,然后还要在两个序列中间插入还未使用过的最小正整数。

例如,序列 [1] 经过一次扩充后,得到序列 [1,2,1],再经过一次扩充后,得到序列 [1,2,1,3,1,2,1]。

现在,请你计算在经过 n−1 次扩充后,序列的第 k 个元素的值是多少?(元素从 1 开始编号)

输入格式
共一行,包含两个整数 n 和 k。

输出格式
输出一个整数,表示经过 n−1 次扩充后,序列的第 k 个元素的值。

数据范围
对于前三个测试点,1≤n,k≤10。
对于全部测试点,1≤n≤50,1≤k≤2n−1。

输入样例1:
3 2
输出样例1:
2
输入样例2:
4 8
输出样例2:
4
样例解释
对于样例 1,经过 2 次扩充,得到序列 [1,2,1,3,1,2,1],其第 2 个元素为 2。

对于样例 2,经过 3 次扩充,得到序列 [1,2,1,3,1,2,1,4,1,2,1,3,1,2,1],其第 8 个元素为 4。

#利用递推来做超时,因为扩充后的list太大了,导致list之间的加法复杂度太高,报错MemoryError
# n,k = map(int, input().split())

# x = [1]
# num = [2]

# for i in range(0,n):
#     x = x + num
#     x = x + x[0:-1]
#     num[0] = num[0] + 1
# print(x[k-1])



# 只能用递归来做,自顶向下的思考,递归的次数最多只有50次,而且不需要保存列表
def f(n,k):
    part = 2**(n-1) -1 
    if k == part + 1:
        return n;
    elif k <= part:
        return f(n-1,k)
    else:
        return f(n-1, k-part-1)

n,k = map(int, input().split())
print(f(n,k))

题目2:递推数列

给定 a0,a1,以及 an=p×an−1+q×an−2 中的 p,q。

这里 n≥2。

求第 k 个数 ak 对 10000 的模。

输入格式
输入包括 5 个整数:a0、a1、p、q、k。

输出格式
第 k 个数 ak 对 10000 的模。

数据范围
1≤a0,a1,p,q,k≤10000
输入样例:
20 1 1 14 5
输出样例:
8359

# 递归做会超时,因为递归的次数太多了,比如k若=9999,需要递归9999次

a0,a1,p,q,k =  map(int, input().split())
# 提前对10000取模的目的是减少计算量,变成10000以下的数进行计算,根据数学性质,先取模和最后取模不影响结果
a0 = a0 % 10000
a1 = a1 % 10000
p = p % 10000
q = q % 10000

def f(x):
    if x == 0:
        return a0 
    elif x == 1:
        return a1 
    else:
        return (p*f(x-1) + q*f(x-2)) 
    
res = f(k)

print(res)

# 只能用递推做,对于这个题来说,递推只是一个数组,所以用递推做更快,不会超时
# a0,a1,p,q,k =  map(int, input().split())

# a = [0 for i in range(k + 1)]
# a[0] = a0 %10000
# a[1] = a1 %10000

# for k in range(2,k+1):
#     a[k] = p*a[k-1] + q*a[k-2]
# print(a[k]%10000)
 


递推递归是两种不同的算法策略,它们在定义、实现方式、执行流程和性能等方面存在明显差异。 ### 定义概念 递归是指在函数的定义中使用函数自身的方法,通过不断调用自身来解决规模更小的子问题,直到达到递归出口条件。例如斐波那契数列的递归实现,通过 `fib(n) = fib(n - 1) + fib(n - 2)` 不断调用自身求解,递归出口为 `n == 0` 时返回 `0`,`n == 1` 时返回 `1` [^2]。 递推则是从已知的初始条件出发,通过递推关系式逐步推导出后续的结果,是一种由前向后的推导过程。在斐波那契数列的递推实现中,使用数组 `dp` 存储中间结果,从初始的 `dp[0] = 0` 和 `dp[1] = 1` 开始,通过 `dp[i] = dp[i - 1] + dp[i - 2]` 逐步计算出 `dp[n]` [^2]。 ### 实现方式 递归的实现通常使用函数调用自身,代码简洁直观,但可能会导致大量的重复计算,并且存在栈溢出的风险。以下是斐波那契数列的递归实现代码: ```java class Solution { public int fib(int n) { if(n == 0) return 0; if(n == 1) return 1; return fib(n - 1) + fib(n - 2); } } ``` 递推的实现一般使用循环结构,如 `for` 或 `while` 循环,通过迭代计算出结果。以下是斐波那契数列的递推实现代码: ```java class Solution { public int fib(int n) { int[] dp = new int[n + 1]; if(n == 0) dp[0] = 0; else if(n == 1) dp[1] = 1; else { dp[0] = 0; dp[1] = 1; for(int i = 2; i <= n; i++) { dp[i] = dp[i - 1] + dp[i - 2]; } } return dp[n]; } } ``` ### 执行流程 递归的执行流程是先不断地深入调用自身,直到达到递归出口,然后再逐步返回结果。在计算斐波那契数列 `fib(n)` 时,会先递归计算 `fib(n - 1)` 和 `fib(n - 2)`,而计算 `fib(n - 1)` 时又会递归计算 `fib(n - 2)` 和 `fib(n - 3)`,以此类推,存在大量的重复计算。 递推的执行流程是从初始条件开始,按照递推关系式依次计算出后续的结果,不存在重复计算的问题。在计算斐波那契数列时,从 `dp[0]` 和 `dp[1]` 开始,依次计算 `dp[2]`、`dp[3]` 直到 `dp[n]`。 ### 性能差异 递归由于存在大量的重复计算,时间复杂度较高,为 $O(2^n)$,空间复杂度也较高,主要是递归调用栈的开销。递推的时间复杂度一般为 $O(n)$,空间复杂度可以优化到 $O(1)$,性能上通常优于递归。 ### 应用场景 递归适用于问题可以分解为规模更小的子问题,且子问题的结构原问题相似的情况,如树的遍历、图的深度优先搜索等。递推适用于有明显的递推关系式和初始条件的问题,如动态规划问题、数列计算等。 ### 语言实现差异 在 Python 中,由于无法在语言中实现尾递归优化,通常采用 `for`、`while` 等特殊结构代替递归的表述 [^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KKdlg

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值