数据结构与算法之美——基础算法

196 阅读2分钟

基础算法之美

递归

递归是一种应用非常广的一种算法.后面涉及的DFS 深度优先搜索、前中后序二叉树遍历等都是用递归算法计算

举个简答的例子理解什么是递归

现在很多 App 都有这个功能“推荐注册返佣金”。 这个功能中,用户 A 推荐用户 B 来注册,用户 B 又推荐了用户 C 来注册。我们可以说,用户 C 的“最终推荐人”为用户 A,用户 B 的“最终推荐人”也为用户 A,而用户 A 没有“最终推荐人”。 问题:那么如何计算C的最终推荐人呢? 一般设计的sql结构是这样的 actor_id 表示用户 id,referrer_id 表示推荐人 id。 解答:为了能找到最终推荐人,需要首先使用C找到他的“上级推荐人”B,再用B找到他的“上级推荐人”A.然后发现A并没有“上级推荐人”,那么A就是“最终推荐人”

所以这种重复执行一个子操作获取一个父结果的方式就叫做递归 递归公式:

f(n)=f(n-1)+1 其中,f(1)=1

递归的三个条件

  • 一个问题的解可以分解为几个子问题的解:“获取上级推荐人”
  • 这个问题与分解之后的子问题,除了数据规模不同,求解思路完全一样:“C获取上级推荐人和B获取上级推荐人操作是一样的”
  • 存在递归终止条件:“A没有上级推荐人”

代码实现递归


int f(int n) {
  if (n == 1) return 1;
  if (n == 2) return 2;
  return f(n-1) + f(n-2);
}

防止堆栈溢出的实现方式


// 全局变量,表示递归的深度。
int depth = 0;

int f(int n) {
  ++depth;
  if (depth > 1000) throw exception;
  
  if (n == 1) return 1;
  return f(n-1) + 1;
}

防止重复计算 比如获取f(4)需要获取f(2),获取f(3)也需要获取f(2)这个时候就可以不用计算f(2)而是直接使用其他的方式获取.


public int f(int n) {
  if (n == 1) return 1;
  if (n == 2) return 2;
  
  // hasSolvedList可以理解成一个Map,key是n,value是f(n)
  if (hasSolvedList.containsKey(n)) {
    return hasSolvedList.get(n);
  }
  
  int ret = f(n-1) + f(n-2);
  hasSolvedList.put(n, ret);
  return ret;
}

编写递归代码的姿势

  1. 推导递归公式(数学基础)
  2. 找出最终条件
  3. 编写递归代码

1. 获取最终推荐人

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