难度标识:
⭐:简单,⭐⭐:中等,⭐⭐⭐:困难。
tips:这里的难度不是根据LeetCode难度定义的,而是根据我解题之后体验到题目的复杂度定义的。
1.二叉树的中序遍历 ⭐
见上篇文章:LeetCode热题100二叉树题解析(上)
2.二叉树的最大深度 ⭐
思路
从上篇文章我们学到了层序遍历,用的时候来了,不过有点大材小用了,但也没关系。找最大深度,我们直接写个层序遍历,然后结果返回层序遍历结果的长度就是深度。
代码
var maxDepth = function (root) {
if (!root) return 0
const q = [root], res = []
while (q.length) {
const levelSize = q.length, levelRes = []
for (let i = 0; i < levelSize; i++) {
const node = q.shift()
levelRes.push(node.val)
if (node.left) q.push(node.left)
if (node.right) q.push(node.right)
}
res.push(levelRes)
}
return res.length
};
上面是一个层序遍历的写法,我们可以优化一下,因为levelRes数组和res数组存的数据都没用上,只是为了计数用,所以我们可以优化一下。
var maxDepth = function (root) {
if (!root) return 0
const q = [root];
let count = 0
while (q.length) {
const levelSize = q.length
for (let i = 0; i < levelSize; i++) {
const node = q.shift()
if (node.left) q.push(node.left)
if (node.right) q.push(node.right)
}
count++
}
return count
};
3.翻转二叉树 ⭐
思路
这题我们可以通过一个前序遍历,然后再遍历的过程中将左子树和右子树互换位置即可,只要掌握了上一章讲的前序遍历,就是很简单的一道题。
代码
var invertTree = function (root) {
if (!root) return root
const stk = [root];
while (stk.length) {
const node = stk.pop();
[node.left, node.right] = [node.right, node.left]
if (node.right) stk.push(node.right)
if (node.left) stk.push(node.left)
}
return root
};
4.对称二叉树 ⭐
思路
这题的思路就是迭代地使用一个栈来检查树是否是轴对称的。具体来说,这种方法使用了一个先进后出的栈结构来代替递归过程,对比每一对可能的对称节点。每次从栈中取出一对节点,如果这两个节点都存在,且它们的值相同,那么就将它们的子节点按照对称的顺序压入栈中。如果这两个节点有一个存在而另一个不存在,或者它们的值不同,那么树就不是对称的。当然这题也可以直接用递归做,递归相对而言代码量会少一些。
代码
var isSymmetric = function (root) {
if (!root) return false
const stk = [root.left, root.right]
while (stk.length) {
const node1 = stk.pop(), node2 = stk.pop()
if (node1 && node2) {
if (node1.val !== node2.val) {
return false
}
stk.push(node1.left, node2.right, node1.right, node2.left)
} else if (node1 || node2) {
return false
}
}
return true
};
5.二叉树的直径 ⭐
思路
解决这个问题的核心思想是递归。对于每个节点,它的直径是左子树的高度和右子树的高度之和。所以,我们可以从树的底部开始,递归地为每个节点计算它的直径,并更新一个全局变量,该变量保存迄今为止找到的最大直径。
代码
var diameterOfBinaryTree = function (root) {
let count = 0
function maxDiameter(node) {
if (!node) return 0
let left = maxDiameter(node.left)
let right = maxDiameter(node.right)
count = Math.max(count, left + right)
return Math.max(left, right) + 1
}
maxDiameter(root)
return count
};
6.二叉树的层序遍历 ⭐
见上篇文章:LeetCode热题100二叉树题解析(上)
7.将有序数组转换为二叉搜索树 ⭐
思路
这题的核心思想还是递归。题目说需要构造一个高度平衡的二叉搜索树,那就应该能想到,取数组的中间值作为根节点就能保证构造的是一棵高度平衡的二叉树。
代码
var sortedArrayToBST = function (nums) {
if (!nums.length) return null
function buildTree(left, right) {
if (left > right) return null
let mid = Math.floor((left + right) / 2)
let root = new TreeNode(nums[mid])
root.left = buildTree(left, mid - 1)
root.right = buildTree(mid + 1, right)
return root
}
return buildTree(0, nums.length - 1)
};
8.验证二叉搜索树 ⭐
思路
这题我们可以使用中序遍历,因为中序遍历是左中右,而二叉搜索树是左 < 中 < 右,然后将遍历结果做一个判断,只要结果数组的第i位大于等于第i+1位,那么就不是二叉搜索树。
代码
var isValidBST = function (root) {
if (!root) return false
const stk = [], res = []
let node = root
while (node || stk.length) {
while (node) {
stk.push(node)
node = node.left
}
node = stk.pop()
res.push(node.val)
node = node.right
}
for (let i = 0; i < res.length - 1; i++) {
if (res[i] >= res[i + 1]) {
return false
}
}
return true
};
9.二叉搜索树中第K小的元素 ⭐
思路
这题跟上面那题其实是一样的,直接中序遍历,然后结果数组的第 k-1 位就是第 k 个最小元素。
代码
var kthSmallest = function (root, k) {
if (!root) return null
const stk = [], res = []
let node = root
while (node || stk.length) {
while (node) {
stk.push(node)
node = node.left
}
node = stk.pop()
res.push(node.val)
node = node.right
}
return res[k - 1]
};
10.二叉树的右视图 ⭐
思路
这题直接使用层序遍历,收集每层的最后一个元素即可。
代码
var rightSideView = function (root) {
if (!root) return []
const q = [root], res = []
while (q.length) {
const levelSize = q.length
for (let i = 0; i < levelSize; i++) {
const node = q.shift()
if (i === levelSize - 1) {
res.push(node.val)
}
if (node.left) q.push(node.left)
if (node.right) q.push(node.right)
}
}
return res
};
11.二叉树展开为链表 ⭐
思路
这题使用前序遍历一下二叉树,并在遍历的过程中将二叉树构造成链表即可。
代码
var flatten = function (root) {
if (!root) return null
const stk = [root];
let prev = null
while (stk.length) {
const node = stk.pop()
if (prev) {
prev.right = node
prev.left = null
}
if (node.right) stk.push(node.right)
if (node.left) stk.push(node.left)
prev = node
}
return root
};
12.从前序与中序遍历序列构造二叉树 ⭐
思路
这题的基本思路还是使用递归。然后我们可以从前序中取根的值,从中序中通过刚才取到的根的值去取左右的值即可。
代码
var buildTree = function (preorder, inorder) {
if (!preorder.length || !inorder.length) return null
let rootVal = preorder[0]
let index = inorder.indexOf(rootVal)
let root = new TreeNode(rootVal)
root.left = buildTree(preorder.slice(1, index + 1), inorder.slice(0, index))
root.right = buildTree(preorder.slice(index + 1), inorder.slice(index + 1))
return root
};
13.路径总和 III ⭐⭐
思路
看见这题是不是有种似曾相识的感觉。如果看过我前面的这篇文章,这里有题和为 K 的子数组,使用的是 前缀和 方法解的,这题其实也是一样的,稍微改一下即可。
代码
var pathSum = function (root, targetSum) {
let count = 0;
const map = new Map()
map.set(0, 1)
function maxPath(node, preSum) {
if (!node) return 0
preSum += node.val
count += map.get(preSum - targetSum) || 0
map.set(preSum, (map.get(preSum) || 0) + 1)
maxPath(node.left, preSum)
maxPath(node.right, preSum)
map.set(preSum, map.get(preSum) - 1)
}
maxPath(root, 0)
return count
};
14.二叉树的最近公共祖先 ⭐
思路
解这题使用递归的方法即可。如果当前节点为空,返回null。如果当前节点是p或q中的一个,返回该节点。递归检查当前节点的左子树和右子树。
代码
var lowestCommonAncestor = function (root, p, q) {
if (!root || root === p || root === q) return root
let left = lowestCommonAncestor(root.left, p, q)
let right = lowestCommonAncestor(root.right, p, q)
if (left && right) {
return root
}
if (left) {
return left
}
if (right) {
return right
}
return null
};
15.二叉树中的最大路径和 ⭐⭐
思路
解这题的思路同样是使用递归。
要找到二叉树中的最大路径和,我们需要使用递归来探索每个节点并同时跟踪两个值:
-
当前节点作为路径的一部分的最大路径和。这意味着这条路径可能会继续延伸到父节点。
-
包括当前节点的最大路径和。这意味着这条路径从当前节点的左子节点经过当前节点到右子节点。
代码
var maxPathSum = function (root) {
let maxSum = Number.MIN_SAFE_INTEGER
function pathSum(node) {
if (!node) return 0
let leftMax = Math.max(pathSum(node.left), 0)
let rightMAx = Math.max(pathSum(node.right), 0)
maxSum = Math.max(maxSum, leftMax + rightMAx + node.val)
return Math.max(leftMax, rightMAx) + node.val
}
pathSum(root)
return maxSum
};
热题100的二叉树的题目到这里就全部解完了,总共15题,只要掌握了二叉树的
4种遍历方式,然后还会递归的思想,这些题目基本上都能理解解决,相对而言,二叉树类型的题目还是比较简单的,而且也比较容易理解,难度不大。