提示:努力生活,开心、快乐的一天
文章目录
513.找树左下角的值
💡解题思路
- 首先明确一个概念:树的最后一行找到最左边的值,可能是左节点,也可能是右节点
- 递归法:三部曲
- 确定递归函数的参数和返回值:要遍历的树的根节点、当前节点的深度、记录最大深度、记录最大深度最左节点的数值
- 确定终止条件:当遇到叶子节点的时候,就需要统计一下最大的深度了,所以需要遇到叶子节点来更新最大深度
- 确定单层递归的逻辑:遍历左右子树、不需要中
- 迭代法:只需要记录最后一行第一个节点的数值就可以了
🤔遇到的问题
- 迭代法记录数值遇到了问题
- 递归法不需要遍历中
💻代码实现
递归法
var findBottomLeftValue = function(root) {
//首先考虑递归遍历 前序遍历 找到最大深度的叶子节点即可
//最大深度
let maxPath = 0
//最大深度对应的节点值
let resNode = null
// 1. 确定递归函数的函数参数
//树的根节点及节点当前深度
const getDfs = (node,curPath)=>{
// 2. 确定递归函数终止条件
//该节点给叶子结点
if(node.left===null&&node.right===null){
if(curPath>maxPath){
maxPath = curPath
resNode = node.val
}
}
//单层逻辑,左右
node.left && getDfs(node.left,curPath+1)
node.right && getDfs(node.right,curPath+1)
}
getDfs(root,1)
return resNode
};
迭代法
var search = function (nums, target) {
var findBottomLeftValue = function(root) {
//最大深度对应的节点值
let resNode = null
if(!root) return null
let queue = []
queue.push(root)
while(queue.length){
let len = queue.length
for(let i=0;i<len;i++){
let node = queue.shift()
if(i===0){
resNode = node.val
}
node.left&&queue.push(node.left)
node.right&&queue.push(node.right)
}
}
return resNode
};
🎯题目总结
- 明确树的最后一行找到最左边的值,可能是左节点,也可能是右节点
- 递归法找到终止便利的条件即可
- 迭代法非常的方便,只需记录每一层的第一个值即可
112. 路径总和
💡解题思路
- 递归法:三部曲
- 确定递归函数的参数和返回类型:需要二叉树的根节点,还需要一个计数器,这个计数器用来计算二叉树的一条边之和是否正好是目标和
- 确定终止条件:不要去累加然后判断是否等于目标和,那么代码比较麻烦,可以用递减,让计数器count初始为目标和,然后每次减去遍历路径节点上的数值。如果最后count == 0,同时到了叶子节点的话,说明找到了目标和。如果遍历到了叶子节点,count不为0,就是没找到。
- 确定单层递归的逻辑:因为终止条件是判断叶子节点,所以递归的过程中就不要让空节点进入递归了。递归函数是有返回值的,如果递归函数返回true,说明找到了合适的路径,应该立刻返回。
- 迭代法:栈里一个元素不仅要记录该节点指针,还要记录从头结点到该节点的路径数值总和
🤔遇到的问题
- 递归法需要注意,递归是有返回值的
- 迭代法:从头结点到该节点的路径数值总和相加忘记计算了
💻代码实现
递归法
var hasPathSum = function(root, targetSum) {
if(!root) return false
const traversal = (node,cursum)=>{
// 遇到叶子节点,并且计数为0
if(!node.left&&!node.right&&cursum===0) return true
// 遇到叶子节点而没有找到合适的边(计数不为0),直接返回
if(!node.left&&!node.right&&cursum!==0) return false
// 左(空节点不遍历).遇到叶子节点返回true,说明其叶子结点满足条件,则直接返回true
if(node.left&&traversal(node.left,cursum-node.left.val)) return true
// 右(空节点不遍历)
if(node.right&&traversal(node.right,cursum-node.right.val)) return true
return false
}
return traversal(root,targetSum-root.val)
};
迭代法
var hasPathSum = function(root, targetSum) {
if(!root) return false
let nodeArr = [root]
let valArr = [0]
while(nodeArr.length){
//当前节点的指针
let curNode = nodeArr.shift()
//从头结点到该节点的路径数值总和
let curVal = valArr.shift()
curVal += curNode.val;
// 为叶子结点,且和等于目标数,返回true
if(!curNode.left&&!curNode.right&&curVal===targetSum){
return true
}
// 左节点,将当前的数值也对应记录下来
if(curNode.left){
nodeArr.push(curNode.left)
valArr.push(curVal)
}
// 右节点,将当前的数值也对应记录下来
if(curNode.right){
nodeArr.push(curNode.right)
valArr.push(curVal)
}
}
return false
};
🎯题目总结
递归函数什么时候需要返回值?什么时候不需要返回值?这里总结如下三点:
- 如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。(这种情况就是113.路径总和ii)
- 如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。(这种情况我们在236. 二叉树的最近公共祖先 (opens new window)中介绍)
- 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(本题的情况)
106.从中序与后序遍历序列构造二叉树
💡解题思路
- 如果数组大小为零的话,说明是空节点了
- 如果不为空,那么取后序数组最后一个元素作为节点元素。
- 找到后序数组最后一个元素在中序数组的位置,作为切割点
- 切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
- 切割后序数组,切成后序左数组和后序右数组
- 递归处理左区间和右区间
此时有一个很重的点,就是中序数组大小一定是和后序数组的大小相同的(这是必然)
🤔遇到的问题
- 在进行切割的时候,边界没有处理好
💻代码实现
递归法
var buildTree = function(inorder, postorder) {
if(!inorder.length) return null
//从后续中获取根节点
let rootVal = postorder.pop()
//获取根节点在中序遍历中的位置
let rootIndex = inorder.indexOf(rootVal)
let root = new TreeNode(rootVal)
//分割中序遍历的左右子树
let leftInTree = inorder.slice(0,rootIndex)
let rightInTree = inorder.slice(rootIndex+1)
//分割后续遍历的左右子树
let leftPosTree = postorder.slice(0,rootIndex)
let rightPosTree = postorder.slice(rootIndex)
//递归处理左区间和右区间
root.left = buildTree(leftInTree,leftPosTree)
root.right = buildTree(rightInTree,rightPosTree)
return root
};
🎯题目总结
前序和中序可以唯一确定一棵二叉树。
后序和中序可以唯一确定一棵二叉树。
前序和后序不能唯一确定一棵二叉树!
🎈今日心得
还不错,只是拓展的题目因为思路一样,所以没有记录博客