10.讲递归:如何用三行代码找到“最终推荐人”

本文深入探讨了递归的概念、条件和实现,强调了递归代码中需要注意的堆栈溢出和重复计算问题,并提供了如何将递归转换为非递归代码的实例。此外,针对给定的用户推荐系统问题,提出了查找用户最终推荐人的解决方案,同时指出在大数据量下可能出现的堆栈溢出和循环引用问题。
摘要由CSDN通过智能技术生成

问题:其中actor_id表示用户id,referrer_id表示推荐人id。
在这里插入图片描述
给定一个用户ID,如何查找这个用户的“最终推荐人”?

1. 如何理解“递归”?

字面意思:去的过程,递,回来的过程,归。

2. 递归需要满足的三个条件

  • 1.一个问题的解可以分解为几个子问题的解
  • 2.这个问题与分解之后的子问题,除了数据规模不同,求解思路完全一样
  • 3.存在递归终止条件

3.如何编写递归代码?

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

例子:假如这里有n个台阶,每次你可以跨1个台阶或者2个台阶,请问走这n个台阶有多少种走法?

递推公式:
分成两类,可以是一步一个台阶,也可以是一步两个台阶。

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

终止条件
如果是一个台阶,只有一种走法,f(1) = 1, 如果是两个台阶,f(2) = f(1) + f(0), f(0) = 1, 不符合情况。所以终止条件是f(1) = 1,f(2) = 2.

提示:
如果一个问题A可以分解为若干子问题B、C、D,你可以假设子问题B、C、D已经解决,在此基础上思考如何解决问题A。而且,你只需要思考问题A与子问题B、C、D两层之间的关系即可,不需要一层一层往下思考子问题与子子问题,子子问题与子子子问题之间的关系。屏蔽掉递归细节,这样子理解起来就简单多了。

4. 递归代码要警惕堆栈溢出

系统栈或者虚拟机栈空间一般都不大。如果递归求解的数据规模很大,调用层次很深,一直压入栈,就会有堆栈溢出的风险。

如何避免出现堆栈溢出:限制递归调用的最大深度的方式

5. 递归代码要警惕重复计算

在这里插入图片描述
可以用散列表保存已经求解的值。

另外的问题:函数调用的数量较大,有客观的时间成本;空间复杂度高。

6. 怎么将递归代码改写为非递归代码?

递归的都可以改成非递归。

之前迈台阶的:其实就是模拟递归。

int f(int n) {
  if (n == 1) return 1;
  if (n == 2) return 2;
  
  int ret = 0;
  int pre = 2;
  int prepre = 1;
  for (int i = 3; i <= n; ++i) {
    ret = pre + prepre;
    prepre = pre;
    pre = ret;
  }
  return ret;
}

7. 解答开篇

long findRootReferrerId(long actorId) {
  Long referrerId = select referrer_id from [table] where actor_id = actorId;
  if (referrerId == null) return actorId;
  return findRootReferrerId(referrerId);
}

问题:

  • 数据量大,存在堆栈溢出问题
  • 脏数据问题,A->B->C->A. 环的存在,导致死循环。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值