力扣关于树的题目(三)(一般二叉树+套路总结)
二叉树的属性问题
1. 101. 对称二叉树
检查自己,左子树,右子树
/**
* @param {TreeNode} root
* @return {boolean}
*/
var isSymmetric = function(root) {
const check = (left,right) => {
if (left === null && right === null) {
return true;
}
if (left === null || right === null) {
return false;
}
let leftData = check(left.left,right.right);
let rightData = check(right.left,left.right);
return leftData && rightData && (left.val === right.val);
}
return check(root.left,root.right)
};
2. 104. 二叉树的最大深度
套路,左子树的深度和右子树的深度,最大的+1
/**
* @param {TreeNode} root
* @return {number}
*/
var maxDepth = function(root) {
let deep = 0;
if (!root) {
return deep;
}
const findDeep = (root) => {
if (!root) {
return 0;
}
let left = findDeep(root.left);
let right = findDeep(root.right);
return Math.max(left,right)+1;
}
return findDeep(root);
};
3. 111. 二叉树的最小深度
跟上一题一样,套路,左右两边小的+1
/**
* @param {TreeNode} root
* @return {number}
*/
var minDepth = function(root) {
const findMin = (root) => {
if(!root){
return 0;
}
if(root.left == null && root.right == null){
return 1;
}
let min = Infinity;
if(root.left){
min = Math.min(min,findMin(root.left))
}
if(root.right){
min = Math.min(min,findMin(root.right))
}
return min + 1;
}
return findMin(root);
};
4. 222. 完全二叉树的节点个数
套路,左边+右边+1
/**
* @param {TreeNode} root
* @return {number}
*/
var countNodes = function(root) {
const count = (root) => {
if (!root) {
return 0;
}
let leftData = count(root.left);
let rightData = count(root.right);
return leftData + rightData + 1;
}
return count(root);
};
5. 110. 平衡二叉树
套路,向左边要信息,右边要信息,再判断返回
/**
* @param {TreeNode} root
* @return {boolean}
*/
var isBalanced = function(root) {
function returnData(is, height) {
this.isBan = is;
this.height = height;
}
const check = (root) => {
if (!root){
return new returnData(true,0);
}
let leftData = check(root.left);
let rightData = check(root.right);
let is = true;
if (!leftData.isBan || !rightData.isBan || Math.abs(leftData.height-rightData.height) > 1) {
is = false;
}
return new returnData(is,Math.max(leftData.height,rightData.height)+1);
}
return check(root).isBan;
};
6. 257. 二叉树的所有路径
这题有点不一样,像全排列,回溯
/**
* @param {TreeNode} root
* @return {string[]}
*/
var binaryTreePaths = function(root) {
let res = [];
let path = [];
function changeToStr() {
let str = "";
for (let i = 0; i < path.length; i++) {
if (i === path.length-1) {
str = str.concat(path[i]);
} else {
str = str.concat(path[i] + "->");
}
}
return str;
}
const backtrace = (root) => {
if (root) {
path.push(root.val);
}
if (!root.left && !root.right){
res.push(changeToStr());
return;
}
if (root.left){
backtrace(root.left)
path.pop();
}
if (root.right) {
backtrace(root.right);
path.pop();
}
}
backtrace(root);
return res;
};
7. 404. 左叶子之和
这里实际上还是遍历,遍历到所有的节点,看它是否存在左节点,而且是叶子节点
/**
* @param {TreeNode} root
* @return {number}
*/
var sumOfLeftLeaves = function(root) {
let sum = 0;
function checkLeave(root) {
if (!root.left && !root.right) {
return true;
}else {
return false;
}
}
const bianli = (root) => {
if(!root){
return;
}
if(root.left && checkLeave(root.left)){
sum += root.left.val;
}
bianli(root.left);
bianli(root.right);
}
bianli(root)
return sum;
};
8. 112. 路径总和
回溯问题,后面应该会单独再归纳回溯的专题
/**
* @param {TreeNode} root
* @param {number} targetSum
* @return {boolean}
*/
var hasPathSum = function(root, targetSum) {
if(!root) {
return false;
}
function checkLeave(root) {
if (!root.left && !root.right){
return true;
}else {
return false;
}
}
// backtrace
const check = (root,sum) => {
if (checkLeave(root) && sum === 0){
return true;
}
if (checkLeave(root)) {
return false;
}
if (root.left) {
if (check(root.left,sum- root.left.val)){
return true;
}
}
if (root.right) {
if (check(root.right,sum- root.right.val)){
return true;
}
}
return false;
}
return check(root,targetSum-root.val);
};
二叉树的修改和构造问题
套路:
修改:找到对应的节点,修改左右子树
构造:先构造根节点,再递归构造左右子树
1. 226. 翻转二叉树
遍历,每一个节点都交换它的左右节点
/**
* @param {TreeNode} root
* @return {TreeNode}
*/
var invertTree = function(root) {
// if (!root) {
// return root;
// }
function swap(root) {
let tmp = root.left;
root.left = root.right;
root.right = tmp;
}
const order = (root) => {
if (!root) {
return;
}
if (root.left || root.right) {
swap(root);
}
order(root.left);
order(root.right)
}
order(root);
return root;
};
2. 106. 从中序与后序遍历序列构造二叉树
在后序找到根节点,再根据中序构造左右子树
/**
* @param {number[]} inorder
* @param {number[]} postorder
* @return {TreeNode}
*/
var buildTree = function(inorder, postorder) {
if (postorder.length === 0) {
return null;
}
let root = new TreeNode(postorder[postorder.length-1]);
let mid = inorder.findIndex((number) => number === root.val);
root.left = buildTree(inorder.slice(0,mid),postorder.slice(0,mid));
root.right = buildTree(inorder.slice(mid+1,inorder.length),postorder.slice(mid,postorder.length-1));
return root;
};
3. 105. 从前序与中序遍历序列构造二叉树
跟上一题一样,在前序找到根节点,再根据中序构造左右子树
/**
* @param {number[]} preorder
* @param {number[]} inorder
* @return {TreeNode}
*/
var buildTree = function(preorder, inorder) {
if (preorder.length === 0) {
return null;
}
let root = new TreeNode(preorder[0]);
let mid = inorder.findIndex((number) => number === root.val);
root.left = buildTree(preorder.slice(1,mid+1),inorder.slice(0,mid));
root.right = buildTree(preorder.slice(mid+1,preorder.length),inorder.slice(mid+1,inorder.length));
return root;
};
4. 654. 最大二叉树
套路,找到对应的节点构造根,再构造左右子树
/**
* @param {number[]} nums
* @return {TreeNode}
*/
var constructMaximumBinaryTree = function(nums) {
function getMax(start, end) {
let res = start;
for (let i = start; i <= end; i++) {
if (nums[i]>nums[res]) {
res = i;
}
}
return res;
}
const backtrace = (start=0,end=nums.length-1) => {
if (start > end){
return null;
}
let index = getMax(start,end);
let root = new TreeNode(nums[index],backtrace(start,index-1),backtrace(index+1,end))
return root;
}
return backtrace();
};
5. 617. 合并二叉树
套路,找到对应的节点构造根,再构造左右子树
/**
* @param {TreeNode} root1
* @param {TreeNode} root2
* @return {TreeNode}
*/
var mergeTrees = function(root1, root2) {
const backtrace = (node1,node2) => {
if (!node1) {
return node2;
}
if (!node2) {
return node1;
}
let root = new TreeNode(node1.val+node2.val,backtrace(node1.left,node2.left),backtrace(node1.right,node2.right))
return root;
}
return backtrace(root1,root2);
};
二叉树的祖先问题
1. 236. 二叉树的最近公共祖先
- map存储所有的节点以及其父节点
- 利用map记录p的所有祖宗节点,一直往上窜,直到root
- 利用map找q的祖宗节点,直到找到的节点出现在p的祖宗节点中
/**
* 最低公共祖先
* @param {TreeNode} root
* @param {TreeNode} p
* @param {TreeNode} q
* @return {TreeNode}
*/
var lowestCommonAncestor = function(root, p, q) {
let map = new Map();
map.set(root,root);
const preOrder = (root) => {
if (!root) {
return;
}
map.set(root.left,root);
map.set(root.right,root);
preOrder(root.left);
preOrder(root.right);
}
preOrder(root);
let hashset = [];
let cur = p;
while (cur !== map.get(cur)) {
hashset.push(cur);
cur = map.get(cur);
}
hashset.push(root)
cur = q;
while (cur !== map.get(cur)) {
let index = hashset.findIndex((item) => item.val === cur.val)
if (index === -1){
cur = map.get(cur);
}else {
return hashset[index];
}
}
return root;
};
优化的代码
- 如果是p和q分别在root的左右两边,也就是说left和right都有值,那么root就是祖先
- left和right有一方是没有值的,说明另一方就是这一方的祖先
/**
* 最低公共祖先
* @param {TreeNode} root
* @param {TreeNode} p
* @param {TreeNode} q
* @return {TreeNode}
*/
var lowestCommonAncestor = function(root, p, q) {
const check = (root,p,q) => {
if(!root || root === p || root === q) {
return root;
}
let left = check(root.left,p,q)
let right = check(root.right,p,q);
if(left && right) {
return root;
}
return left !== null ? left : right;
}
return check(root,p,q)
};
二叉树的套路
- 定义数据类型
- 向左边要信息,要右边要信息
- 然后根据左右的信息,算出自己要返回的信息
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)