递归
我们递归的求解一个问题,在每层递归中应用如下三个步骤:
- 将问题划分为一些子问题,子问题的形式和原问题一样,只是规模更小
- 递归的解决子问题,当子问题足够小(初始值)时,即停止递归
- 将子问题的解合并成原问题的解
斐波那契数列
先来个简单题开个胃:https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof/
按照上面的套路进行分析: - 将问题划分为一些子问题 f(n) = f(n-1) + f(n-2) ,n-2>=0 则初始值是f(0) f(1)
- 子问题足够小(初始值),f(0) = 0 f(1) = 1
- 将子问题合并 递归本身就是在堆栈中保存调用,实现合并的过程。即return fib(n-1) + fib(n-2),递归调用到堆栈的过程
public int fib(int n) {
if (n == 0 ) {
return 0;
}
if (n == 1){
return 1;
}
return fib(n - 1) + fib(n - 2);
}
687. 最长同值路径
来个中等的 https://leetcode-cn.com/problems/longest-univalue-path/
给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值。 这条路径可以经过也可以不经过根节点。
注意:两个节点之间的路径长度由它们之间的边数表示。
第一感觉,题没读懂,关键点认为是下面:
- 两个节点间的路径是啥意思
- 是否只有一条
- 如何找到两个节点间的路径
- 如果都相等会有什么特征,不等又有什么特征
二叉树的基本知识可以看一下大神的博客:https://www.cnblogs.com/sunshineliulu/p/7775063.html
根据题目意思可以明确看出节点间路径的意思,那么剩下的问题也基本好解答了。 - 是否只有一条 ————根据二叉树特性,不走重复路线的话,就只有一条了,否则不是有环了吗
- 如何找到两个节点间的路径————可以从根分别找到两个节点的路径,然后合并一下
- 如果都相等会有什么特征,不等又有什么特征 ————合并后路径上的数都是一样的
分析到这里,感觉更像是列出所有路径,然后去合并,而合并到哪个节点终止也很难说,这样将变成n!种可能,这也太难搞了吧。
我们尝试分析一下是否可以分解为子问题:
(1)路径本身就可以是一个二叉树,只是所有节点都少了一个边
(2)要找的路径必然包含两个相等的节点(父节点和子节点相等),并且可能还有很多个相等
(3)这样变成了对每个节点找向外延伸的最长,这样必然有子问题,例如5(0) - 5(1) - 5(2) - 5(3)- 5(4),我们找2向左延伸的最长,是5(1)向左延伸的最长+1;
代码还是不会写,对照答案进行分析流程,感觉流程有重复子问题,不知道是否可以用DP,欢迎大家留言讨论。
int ans;
public int longestUnivaluePath(TreeNode root) {
ans = 0;
arrowLength(root);
return ans;
}
public int arrowLength(TreeNode node) {
//这里十分巧妙,将null直接返回0,满足了树的遍历条件
if (node == null) return 0;
//当前节点的最长依赖于左节点,也依赖于右节点,满足了后序遍历条件
int left = arrowLength(node.left);
int right = arrowLength(node.right);
int arrowLeft = 0, arrowRight = 0;
//先处理左,把左边的加进来
if (node.left != null && node.left.val == node.val) {
arrowLeft += left + 1;
}
//再处理右,把右边的加进来
if (node.right != null && node.right.val == node.val) {
arrowRight += right + 1;
}
//总长和ans对比,如果当前节点更长时,更新ans
ans = Math.max(ans, arrowLeft + arrowRight);
//返回当前节点的最长延伸
return Math.max(arrowLeft, arrowRight);
}