递归用法总结(二叉树深度为例)

说来惭愧,从业多年,现在却发现很多基础还不牢靠。

晚上刷leetcode,104. Maximum Depth of Binary Tree,直觉是感觉可以用递归来解决。

如上面的二叉树,在3的时候是层1, 下一层到9的时候就是层2,如果向上返回了层数就-1,然后使用一个变量记录最大值。通过示意图可以很简单看到深度是3层。

代码也不复杂,很快就写好。但是面临一个问题,就是当前层数和最大层数,必须要使用全局变量来记录,而在leetcode中,全局变量没法再初始化,所以就不可用。想破脑袋也没想出来怎么解决,后面求助了chatgpt,给的代码也是有这两个全局变量。确实这两个全局变量是绕不过去的。

// 递归计算每个节点的深度
void calculateDepth(TreeNode* root, int currentDepth, int* maxDepth) {
    if (root == NULL) {
        return;
    }

    // 更新最大深度
    *maxDepth = (currentDepth > *maxDepth) ? currentDepth : *maxDepth;

    // 递归计算左子树和右子树的深度
    calculateDepth(root->left, currentDepth + 1, maxDepth);
    calculateDepth(root->right, currentDepth + 1, maxDepth);
}

后面没办法看了答案,原来是需要自底而上。从最底层开始计数。如图:

想来也是非常合理的,其实递归的本质或者从动态规划来说,也是应该自底而上,这样才能拆成更小的独立问题,整个逻辑也更简单。经过拆分就很明显了,当前节点的深度=下面节点最大深度+自身。

f(n) = max(f(n.left), f(n.right)) + 1

f(0) = 1

既然如此,答案就很简单了:

int maxDepth(struct TreeNode* root) {
    if(!root)
        return 0;
    int leftdepth = maxDepth(root->left);
    int rightdepth = maxDepth(root->right);
    if(leftdepth > rightdepth)
        return leftdepth + 1;
    else
        return rightdepth + 1;
}

提交上去看了下,可以通过但是成绩也不是很理想,应该可以加个字典什么的缓存一下。

这里确实思路还是应该优先自下而上Bottom-up的递归

从上面可以看出,动态规划或者说由下而上的递归流程就是三步:

1 确定最优最小单元的解法。就是上面的f(n) = max(f(n.left), f(n.right)) + 1。(注:有的可能是不存在最优最小单元解法的,这个以后再研究吧。)

2 确定出口。就是上面的f(0) = 1

3 优化和剪枝。

算了,就这样吧。挫折+1

====================================================================

今天又看了下资料,到底是Bottom up还是Top down,还是要根据情况。

Top-Down Recursion

  • Also Known as: Divisive or Decrease and Conquer.
  • Approach: It starts with the original problem and breaks it down into smaller subproblems. It solves the original problem by recursively solving the subproblems.
  • Example: Merge Sort is a classic example of a top-down approach, where the array is recursively divided into two halves until the base case is reached.
  • Memoization: Top-down recursion often uses memoization to store the results of already solved subproblems to avoid redundant calculations.

Bottom-Up Recursion

  • Also Known as: Aggregative or Dynamic Programming.
  • Approach: It starts with the simplest subproblem and solves it. It then uses its solution to solve more complex subproblems, building up to the original problem.
  • Example: Fibonacci sequence calculation can be solved using a bottom-up approach by solving the smallest subproblems first and building up to the desired value.
  • Tabulation: Bottom-up recursion often uses tabulation to store the results of subproblems in a table (usually an array or a matrix) to avoid redundant calculations.

https://medium.com/cracking-the-coding-interview-in-ruby-python-and/bottom-up-and-top-down-recursion-explained-with-examples-in-ruby-javascript-and-python-mastering-9fa051ee53ff

总结下来就是

自顶而下递归就是分治算法(狗头,明明很清晰的东西非要搞个新的学名),典型就是合并排序。

自底而上递归就是动态规划(狗头),典型的就是费布那切序列,个人觉得二叉树算深度也可以算。

欧美那边真的很喜欢给算法命名,一些本质很简单的算法也搞个很高大上的名称,有时候这样反而让人望而生畏,增加学习的难度。有时候想想,还好清王朝没接触计算机,要不给遍历给个命名爱新觉罗·努尔哈赤 天命汗爱新觉罗·努尔哈赤规划算法。我看你们还怎么和我天朝PK。。。

最后小感叹一下,在写算法或者是复杂的程序时,一定先在纸上算清楚再动手。出了问题,不要闷着头就去改代码,切记磨刀不误砍柴工,涉及到流程异常先回纸上推演清楚再回去改代码。这点非常重要,非常重要,非常重要。

  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值