数据结构与算法--基础算法

递归

有两个最难以解的知识点,一个是动态规划,一个是递归
深度优先遍历,前中后序二叉树遍历都会用到递归算法

所有的递归问题都可以用递推公式来表示
f(n) = f(n-1)+1,其中f(1)=1
写递归代码最关键的是 写出递推公式,找到终止条件,剩下将递推公式转化为代码就很简单了

递归需要满足三个条件

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

假设有n个台阶,每次可以跨1个台阶或者2个台阶,走这n个台阶有多少走法?

  • 如7个台阶可以,2-2-2-1这样,也可以1-2-1-1-2这样
  • 可以根据第一步的走法把 所有走法分两类,
  • 第一类是第一步走了1个台阶,另一类是第一步走了2个台阶
  • 所以,n个台阶的走法等于
  • 先走1阶后 n-1个台阶的走法 +  先走2阶后 n-2个台阶的走法,公式为
  • f(n) = f(n-1) + f(n-2)
  • 终止条件是f(1)=1,f(2)=2

最后得到的结果是

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

写递归代码的关键

  • 找到如何将大问题分解为小问题的规律, 并且基于此写出递推公式,然后再推敲终止条件,最后将递推公式和终止条件翻译成代码
  • 只要遇到递归,就把它抽象成一个递推公式,不用想一层层的调用关系,不要试图用人脑分解递归的每个步骤

注意事项

  • 递归代码要警惕堆栈溢出
  • 递归代码要警惕重复计算(下台阶问题,可以用hash表保存重复值
  • 递归的空间复杂度很高

下台阶的整个计算分解过程如下图

下台阶改成非递归实现

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;
}

笼统的讲,所有的递归代码都可以改写为迭代循环的非递归写法。
如何做?抽象出递推公式、初始值和边界条件,然后用迭代循环实现。


寻找最终推荐人

/**
   寻找最终推荐人的方式
   不过这段代码有问题
   1.递归可能很深
   2.脏数据情况下可能会死循环
   更高级的处理方式,自动检测A-B-C-A这种环的存在
**/
long findRootReferrerId(long actorId) {
    long referrerId = select referrer_id from [table] where actor_id=actorId;
    if(referrerId == null) return actorId;
    return findRootReferrerId(referrerId);
}


 

猜你喜欢

转载自blog.csdn.net/hixiaoxiaoniao/article/details/88551462