学习目标:
● 513.找树左下角的值
● 112. 路径总和 113.路径总和ii
● 106.从中序与后序遍历序列构造二叉树 105.从前序与中序遍历序列构造二叉树
学习内容:● 513.找树左下角的值
题目链接/文章讲解/视频讲解:https://programmercarl.com/0513.%E6%89%BE%E6%A0%91%E5%B7%A6%E4%B8%8B%E8%A7%92%E7%9A%84%E5%80%BC.html
tip:在树的最后一行找到最左边的值。
层序遍历比递归更好理解
递归版本:
var findBottomLeftValue = function(root) {
//首先考虑递归遍历 前序遍历 找到最大深度的叶子节点即可
let maxPath = 0, resNode = null;
// 1. 确定递归函数的函数参数
const dfsTree = function(node, curPath) {
// 2. 确定递归函数终止条件
if(node.left === null && node.right === null) {
if(curPath > maxPath) {
maxPath = curPath;
resNode = node.val;
}
}
node.left && dfsTree(node.left, curPath+1);
node.right && dfsTree(node.right, curPath+1);
}
dfsTree(root,1);
return resNode;
};
层序遍历:
var findBottomLeftValue = function(root) {
//考虑层序遍历 记录最后一行的第一个节点
let queue = [];
if(root === null) {
return null;
}
queue.push(root);
let resNode;
while(queue.length) {
let length = queue.length;
for(let i = 0; i < length; 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. 路径总和 113.路径总和ii
题目链接/文章讲解/视频讲解:https://programmercarl.com/0112.%E8%B7%AF%E5%BE%84%E6%80%BB%E5%92%8C.html
0112.路径总和
递归
// targetSum = targetSum-root.left.val 注意是root.left.val
/**
* @param {treenode} root
* @param {number} targetsum
* @return {boolean}
*/
let haspathsum = function (root, targetsum) {
// 递归法
const traversal = (node, cnt) => {
// 遇到叶子节点,并且计数为0
if (cnt === 0 && !node.left && !node.right) return true;
// 遇到叶子节点而没有找到合适的边(计数不为0),直接返回
if (!node.left && !node.right) return false;
// 左(空节点不遍历).遇到叶子节点返回true,则直接返回true
if (node.left && traversal(node.left, cnt - node.left.val)) return true;
// 右(空节点不遍历)
if (node.right && traversal(node.right, cnt - node.right.val)) return true;
return false;
};
if (!root) return false;
return traversal(root, targetsum - root.val);
// 精简代码:
// if (!root) return false;
// if (!root.left && !root.right && targetsum === root.val) return true;
// return haspathsum(root.left, targetsum - root.val) || haspathsum(root.right, targetsum - root.val);
};
0113.路径总和-ii
// travelsal(root, targetsum - root.val, [root.val]); // 把根节点放进路径
// res.push([...path]); // 不能写res.push(path), 要深拷贝
递归
let pathsum = function (root, targetsum) {
// 递归法
// 要遍历整个树找到所有路径,所以递归函数不需要返回值, 与112不同
const res = [];
const travelsal = (node, cnt, path) => {
// 遇到了叶子节点且找到了和为sum的路径
if (cnt === 0 && !node.left && !node.right) {
res.push([...path]); // 不能写res.push(path), 要深拷贝
return;
}
if (!node.left && !node.right) return; // 遇到叶子节点而没有找到合适的边,直接返回
// 左 (空节点不遍历)
if (node.left) {
path.push(node.left.val);
travelsal(node.left, cnt - node.left.val, path); // 递归
path.pop(); // 回溯
}
// 右 (空节点不遍历)
if (node.right) {
path.push(node.right.val);
travelsal(node.right, cnt - node.right.val, path); // 递归
path.pop(); // 回溯
}
return;
};
if (!root) return res;
travelsal(root, targetsum - root.val, [root.val]); // 把根节点放进路径
return res;
};
学习内容:● 106.从中序与后序遍历序列构造二叉树 105.从前序与中序遍历序列构造二叉树
题目链接/文章讲解/视频讲解:https://programmercarl.com/0106.%E4%BB%8E%E4%B8%AD%E5%BA%8F%E4%B8%8E%E5%90%8E%E5%BA%8F%E9%81%8D%E5%8E%86%E5%BA%8F%E5%88%97%E6%9E%84%E9%80%A0%E4%BA%8C%E5%8F%89%E6%A0%91.html
1.后序数组为0,空节点
2.后序数组最后一个元素为节点元素
3.寻找中序数组位置作为切割点
4.切中序数组
5.切后序数组
6.递归处理左区间右区间
注意确定切割的标准,是左闭右开,还有左开右闭,还是左闭右闭,这个就是不变量,要在递归中保持这个不变量。
前序和中序可以唯一确定一棵二叉树。
后序和中序可以唯一确定一棵二叉树。
前序和后序不能唯一确定一棵二叉树!,因为没有中序遍历无法确定左右部分,也就是无法分割。
从中序与后序遍历序列构造二叉树
var buildTree = function(inorder, postorder) {
if (!inorder.length) return null;
const rootVal = postorder.pop(); // 从后序遍历的数组中获取中间节点的值, 即数组最后一个值
let rootIndex = inorder.indexOf(rootVal); // 获取中间节点在中序遍历中的下标
const root = new TreeNode(rootVal); // 创建中间节点
root.left = buildTree(inorder.slice(0, rootIndex), postorder.slice(0, rootIndex)); // 创建左节点
root.right = buildTree(inorder.slice(rootIndex + 1), postorder.slice(rootIndex)); // 创建右节点
return root;
};
从前序与中序遍历序列构造二叉树
var buildTree = function(preorder, inorder) {
if (!preorder.length) return null;
const rootVal = preorder.shift(); // 从前序遍历的数组中获取中间节点的值, 即数组第一个值
const index = inorder.indexOf(rootVal); // 获取中间节点在中序遍历中的下标
const root = new TreeNode(rootVal); // 创建中间节点
root.left = buildTree(preorder.slice(0, index), inorder.slice(0, index)); // 创建左节点
root.right = buildTree(preorder.slice(index), inorder.slice(index + 1)); // 创建右节点
return root;
};