说来惭愧,从业多年,现在却发现很多基础还不牢靠。
晚上刷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);
}
后面没办法看了答案,原来是需要自底而上。从最底层开始计数。如图:
想来也是非常合理的,其实递归的本质或者从动态规划来说,也是应该自底而上,这样才能拆成更小的独立问题,整个逻辑也更简单。经过拆分就很明显了,当前节点的深度=下面节点最大深度+自身。
既然如此,答案就很简单了:
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.
总结下来就是
自顶而下递归就是分治算法(狗头,明明很清晰的东西非要搞个新的学名),典型就是合并排序。
自底而上递归就是动态规划(狗头),典型的就是费布那切序列,个人觉得二叉树算深度也可以算。
欧美那边真的很喜欢给算法命名,一些本质很简单的算法也搞个很高大上的名称,有时候这样反而让人望而生畏,增加学习的难度。有时候想想,还好清王朝没接触计算机,要不给遍历给个命名爱新觉罗·努尔哈赤 天命汗爱新觉罗·努尔哈赤规划算法。我看你们还怎么和我天朝PK。。。
最后小感叹一下,在写算法或者是复杂的程序时,一定先在纸上算清楚再动手。出了问题,不要闷着头就去改代码,切记磨刀不误砍柴工,涉及到流程异常先回纸上推演清楚再回去改代码。这点非常重要,非常重要,非常重要。
。