Leetcode 题解 - 树
* 前中后序遍历
144.二叉树的前序遍历
迭代写法
var preorderTraversal = function (root) {
if (!root) return []
let stack = [root]
let ans = []
while(stack.length){
let cur = stack.pop()
ans.push(cur.val)
if (cur.right) {
stack.push(cur.right)
}
if (cur.left) {
stack.push(cur.left)
}
}
return ans
}
递归写法
var preorderTraversal = function (root) {
if (!root) return []
let ans = []
const preorder = function (root, ans) {
if (!root) return
ans.push(root.val)
preorder(root.left, ans)
preorder(root.right, ans)
}
preorder(root, ans)
return ans
}
145.二叉树的后序遍历
迭代写法
var postorderTraversal = function (root) {
if (!root) return []
let stack = [root]
let ans = []
let p = root
while (stack.length) {
let cur = stack[stack.length - 1]
if (cur.right == p || cur.left == p || (cur.right == null && cur.left == null)) {
p = stack.pop()
ans.push(p.val)
} else {
if (cur.right) {
stack.push(cur.right)
}
if (cur.left) {
stack.push(cur.left)
}
}
}
return ans
}
递归写法
var postorderTraversal = function (root) {
if (!root) return []
let ans = []
const postorder = (root, ans) => {
if(!root) return
postorder(root.left, ans)
postorder(root.right, ans)
ans.push(root.val)
}
postorder(root, ans)
return ans
}
94.二叉树的中序遍历
迭代写法
var inorderTraversal = function (root) {
if (!root) return []
const stack = [root]
const ret = []
let left = root.left
let item = null; // stack 中弹出的当前项
while (left) {
stack.push(left)
left = left.left
}
while ((item = stack.pop())) {
ret.push(item.val)
let t = item.right
while (t) {
stack.push(t)
t = t.left
}
}
return ret
}
递归写法
var inorderTraversal = function (root) {
if (!root) return []
let ans = []
const inorder = function (root, ans) {
if (!root) return
inorder(root.left, ans)
ans.push(root.val)
inorder(root.right, ans)
}
inorder(root, ans)
return ans
}
* 序列化构造二叉树
105.从前序与中序遍历序列构造二叉树
var buildTree = function (preorder, inorder) {
if (!preorder.length) return null
// 使用map存储,方便查找
let m = new Map()
for (let i = 0; i < inorder.length; i++) {
m.set(inorder[i], i)
}
const handle = (p_start, p_end, i_start, i_end) => {
if (p_end < p_start) return null
const root = new TreeNode(preorder[p_start])
const mid = m.get(preorder[p_start])
let leftNum = mid - i_start
root.left = handle(p_start + 1, p_start + leftNum, i_start, mid - 1)
root.right = handle(p_start + leftNum + 1, p_end, mid + 1, i_end)
return root
}
return handle(0, preorder.length - 1, 0, inorder.length - 1)
}
106.从中序与后序遍历序列构造二叉树
var buildTree = function (inorder, postorder) {
if (!inorder.length) return null
let m = new Map()
for (let i = 0; i < inorder.length; i++) {
m.set(inorder[i], i)
}
const handle = (i_start, i_end, p_start, p_end) => {
if (p_start > p_end) return null
const root = new TreeNode(postorder[p_end])
const mid = m.get(postorder[p_end])
const leftNum = mid - i_start
root.left = handle(i_start, mid - 1, p_start, p_start + leftNum - 1)
root.right = handle(mid + 1, i_end, p_start + leftNum, p_end - 1)
return root
}
return handle(0, inorder.length - 1, 0, postorder.length - 1)
};
889.根据前序和后序遍历构造二叉树
var constructFromPrePost = function (pre, post) {
if (!pre.length) return null
let m = new Map()
for (let i = 0; i < post.length; i++) {
m.set(post[i], i)
}
const handle = (pre_start, pre_end, post_start, post_end) => {
if (pre_start > pre_end) return null
const root = new TreeNode(pre[pre_start])
if (pre_start == pre_end) return root
const leftNum = m.get(pre[pre_start + 1]) - post_start
root.left = handle(pre_start + 1, pre_start + 1 + leftNum, post_start, post_start + leftNum)
root.right = handle(pre_start + 2 + leftNum, pre_end, post_start + leftNum + 1, post_end - 1)
return root
}
return handle(0, pre.length - 1, 0, post.length - 1)
};
1008.前序遍历构造二叉搜索树
var bstFromPreorder = function (preorder) {
const bulid = function (start, end) {
if (start > end) return null
const node = new TreeNode(preorder[start])
if (start == end) return node
// 比根节点大的值为右节点
let left = end
for (let i = start + 1; i <= end; i++) {
if (preorder[start] < preorder[i]) {
// 找到右节点的起始位置,就可以退出循环
left = i - 1
break
}
}
// 继续递归
node.left = bulid(start + 1, left)
node.right = bulid(left + 1, end)
return node
}
return bulid(0, preorder.length - 1)
}
654.最大二叉树
var constructMaximumBinaryTree = function (nums) {
const buildTree = function (start, end) {
if (start > end) return null
// 找到最大值
let max = -Infinity
let index = 0
for (let i = start; i <= end; i++) {
if (nums[i] > max) {
max = nums[i]
index = i
}
}
const root = new TreeNode(max)
root.left = buildTree(start, index - 1)
root.right = buildTree(index + 1, end)
return root
}
return buildTree(0, nums.length - 1)
}
* BFS遍历
102.二叉树的层序遍历
var levelOrder = function (root) {
if (!root) return []
let ans = []
let queue = [root]
while(queue.length){
let queueLen = queue.length
ans.push([])
for(let i = 0; i< queueLen; i++){
let cur = queue.shift()
ans[ans.length-1].push(cur.val)
if(cur.left) queue.push(cur.left)
if(cur.right) queue.push(cur.right)
}
}
return ans
}
513.找树左下角的值
var findBottomLeftValue = function (root) {
let queue = [root]
let ans
while (queue.length) {
let queueLen = queue.length
for (let i = 0; i < queueLen; i++) {
let cur = queue.shift()
if(cur.left) queue.push(cur.left)
if(cur.right) queue.push(cur.right)
if(i == 0) ans = cur
}
if(queue.length == 0) return ans.val
}
}
116.填充每个节点的下一个右侧节点指针
BFS
var connect = function (root) {
if (!root) return null
let queue = [root]
let last = null
let queueLen
while (queue.length) {
queueLen = queue.length
for (let i = 0; i < queueLen; i++) {
let cur = queue.shift()
if (cur.left) {
queue.push(cur.left)
queue.push(cur.right)
}
if (i) {
last.next = cur
}
last = cur
}
}
return root
}
利用已经生成的链表遍历,由于不使用队列,可以减少内存空间
var connect = function (root) {
if (!root) return null
let last = null
let startNode = null
const handle = (node) => {
if (last) {
last.next = node
}
if (!startNode) {
startNode = node
}
last = node
}
let start = root
while (start) {
startNode = null
last = null
for (let p = start; p != null; p = p.next) {
if (p.left) {
handle(p.left)
handle(p.right)
}
}
start = startNode
}
return root
}
662.二叉树最大宽度
var widthOfBinaryTree = function (root) {
let queue = [root]
let pos = [1]
let left = right = ans = 0
while (queue.length) {
const queueLen = queue.length
if (queueLen == 1) pos = [1]
for (let i = 0; i < queueLen; i++) {
let cur = queue.shift()
let dis = pos.shift()
if (cur.left) {
queue.push(cur.left)
pos.push(2 * dis)
}
if (cur.right) {
queue.push(cur.right)
pos.push(2 * dis + 1)
}
if (i == 0) left = dis
if (i == queueLen - 1) right = dis
}
ans = Math.max(right - left + 1, ans)
}
return ans
}
* DFS遍历
104.二叉树的最大深度
var maxDepth = function (root) {
// 迭代
if (!root) return 0
let queue = [root]
let depth = 0
while (queue.length) {
let queueLen = queue.length
for (let i = 0; i < queueLen; i++) {
let cur = queue.shift()
if (cur.left) queue.push(cur.left)
if (cur.right) queue.push(cur.right)
}
depth++
}
return depth
}
剑指Offer55-I.二叉树的深度
题目和上一题一样,本题补充了另一种写法
var maxDepth = function (root) {
// 递归
if (!root) return 0
const l = maxDepth(root.left)
const r = maxDepth(root.right)
return Math.max(l + 1, r + 1)
};
剑指34.二叉树中和为某一值的路径
前序遍历+回溯
var pathSum = function (root, target) {
if (!root) return []
const dfs = function (root, target, path, res) {
if (!root) return
// 选择
path.push(root.val)
// 判断, 叶子节点
if (!root.left && !root.right && target === root.val) {
res.push([...path])
} else {
// 递归
dfs(root.left, target - root.val, path, res)
dfs(root.right, target - root.val, path, res)
}
// 撤销选择
path.pop()
}
let ans = [], path = []
dfs(root, target, path, ans)
return ans
}
* 二叉树与链表
116.填充每个节点的下一个右侧节点指针
var connect = function (root) {
if (!root) return null
let last = null
let startNode = null
const handle = (node) => {
if (last) {
last.next = node
}
if (!startNode) {
startNode = node
}
last = node
}
let start = root
while (start) {
startNode = null
last = null
for (let p = start; p != null; p = p.next) {
if (p.left) {
handle(p.left)
handle(p.right)
}
}
start = startNode
}
return root
}
117.填充每个节点的下一个右侧节点指针II
var connect = function (root) {
if (!root) return null
let queue = [root]
let queueLen = null
let last = null
while (queue.length) {
queueLen = queue.length
for (let i = 0; i < queueLen; i++) {
let cur = queue.shift()
if(cur.left) queue.push(cur.left)
if(cur.right) queue.push(cur.right)
if(i) last.next = cur
last = cur
}
}
return root
}
* 序列化与反序列化
449.序列化和反序列化二叉搜索树
var serialize = function (root) {
// 后序遍历
// 递归写法
if (!root) return ''
let ans = []
const postTraversal = (root, ans) => {
if (!root) return
postTraversal(root.left, ans)
postTraversal(root.right, ans)
ans.push(root.val)
}
postTraversal(root, ans)
return ans.join(',')
}
var deserialize = function (data) {
if (data.length == 0) return null
post = data.split(',').map(item => Number.parseInt(item))
let inorder = [...post].sort((a, b) => a - b)
// 从后序和中序遍历恢复树结构
let m = new Map()
for (let i = 0; i < inorder.length; i++) {
m.set(inorder[i], i)
}
const buildTree = (i_start, i_end, p_start, p_end) => {
if (i_start > i_end) return null
const root = new TreeNode(post[p_end])
const mid = m.get(post[p_end])
const leftNum = mid - i_start
root.left = buildTree(i_start, mid - 1, p_start, p_start + leftNum - 1)
root.right = buildTree(mid + 1, i_end, p_start + leftNum, p_end - 1)
return root
}
return buildTree(0, inorder.length - 1, 0, post.length - 1)
}
297.二叉树的序列化与反序列化
var serialize = function(root) {
if(!root) return 'X'
const left = serialize(root.left)
const right = serialize(root.right)
return root.val + ',' + left + ',' + right
}
var deserialize = function(data) {
if(data.lenght == 0) return null
data = data.split(',')
const buildTree = (data) => {
let cur = data.shift()
if(cur === 'X') return null
const root = new TreeNode(cur)
root.left = buildTree(data)
root.right = buildTree(data)
return root
}
return buildTree(data)
}
* 其他
1372.二叉树中的最长交错路径
var longestZigZag = function (root) {
if (!root) return 0
let res = -Infinity
// 节点的右拐值 = 1 + 右子节点的左拐值
// 节点的左拐值 = 1 + 左子节点的右拐值
const dfs = function (root) {
if (!root) return [-1, -1]
const [ll, lr] = dfs(root.left)
const [rl, rr] = dfs(root.right)
res = Math.max(res, 1 + lr, 1 + rl)
return [1 + lr, 1 + rl]
}
dfs(root)
return res
}
863.二叉树中所有距离为K的结点
var distanceK = function (root, target, k) {
let targetNode = null
// 使用递归寻找target值的节点
// 并保存目标值的父节点,方便向下查找
const findTarget = function (root, target) {
if (root.val === target) {
targetNode = root
return
}
if (root.left) {
root.left.parent = root
findTarget(root.left, target)
}
if (root.right) {
root.right.parent = root
findTarget(root.right, target)
}
}
findTarget(root, target)
// 目标节点向下遍历
// path用来保存已经访问过的值
let path = []
let ans = []
const findNode = function (node, k) {
// 树遍历完或已经遍历过该节点
if (!node || path.indexOf(node.val) !== -1) {
return
}
path.push(node.val)
if (k == 0){
ans.push(node.val)
}else{
k--
findNode(node.left, k)
findNode(node.right, k)
}
}
findNode(targetNode, k)
// 从目标节点开始向上遍历
while(targetNode.parent && k){
targetNode = targetNode.parent
// 向上走了一步,k--
k--
findNode(targetNode, k)
}
return ans
}
98.验证二叉搜索树
var isValidBST = function (root) {
// 二叉搜索树的中序遍历是递增的
// 此题可简化为求中序遍历结果,并在遍历过程中判断是否递增
// 中序遍历有多种写法,这边是迭代写法
if (!root) return false
let stack = [root]
let left = root.left
while(left){
stack.push(left)
left = left.left
}
let pre = -Infinity
while(stack.length){
let cur = stack.pop()
// 如果非递增,直接返回false
if(pre > cur.val) return false
pre = cur.val
let right = cur.right
while(right){
stack.push(right)
right = right.left
}
}
return true
}
99.恢复二叉搜索树
var recoverTree = function(root) {
// 题目思路:中序遍历过程中交换错误节点
// 题目中提到只有两个节点被交换,因此两个节点要么相邻,要么不相邻
let stack = [root]
let left = root.left
const swap = function (x, y) {
// 交换
let temp = x.val
x.val = y.val
y.val = temp
}
// 左节点先入栈
while(left){
stack.push(left)
left = left.left
}
let pre = null
let x = null, y = null
while(stack.length){
let cur = stack.pop()
if(pre !== null && pre.val > cur.val){
y = cur
if(!x){
x = pre
}else{
// 遍历到第二个错误节点
break
}
}
pre = cur
let right = cur.right
while(right){
stack.push(right)
right = right.left
}
}
swap(x, y)
return root
}
814.二叉树剪枝
var pruneTree = function(root) {
// 由于根节点可能被删除
// 因此需要一个虚拟节点指向根节点
let vnode = new TreeNode(-1)
vnode.left = root
// 后序遍历计算子树的和,删除子树和为0的节点
const dfs = function (root){
if(!root) return 0
const l = dfs(root.left)
const r = dfs(root.right)
if(l == 0) root.left = null
if(r == 0) root.right = null
return root.val + l + r
}
dfs(vnode)
return vnode.left
}
1325.删除给定值的叶子节点
// 删除节点需要用到节点的父节点
// 所以创建一个虚拟节点指向根节点,这样就能操作到根节点
let vnode = new TreeNode(-1)
vnode.left = root
// 设置一个参数parent纪录当前节点的父节点,便于删除
const dfs = function (root, parent, isLeft = true) {
if (!root) return
// 采用后序遍历方便查找
dfs(root.left, root, true)
dfs(root.right, root, false)
// 叶子节点的左右都是null
if(root.val == target && root.left == null && root.right == null){
if(isLeft){
parent.left = null
}else{
parent.right = null
}
}
}
dfs(root, vnode)
return vnode.left
1530.好叶子节点对的数量
解题关键:两个叶子节点的最短路径 = 其中一个叶子节点到最近公共祖先的距离 + 另外一个叶子节点到最近公共祖先的距离
而父到各个叶子节点的距离就是父节点到子节点的距离(1) + 子节点到各个叶子节点的距离
var countPairs = function (root, distance) {
let ans = 0
const dfs = function (root) {
if (!root) return []
if (!root.left && !root.right) {
return [0]
}
let ls = [...dfs(root.left)].map(item => item + 1)
let rs = [...dfs(root.right)].map(item => item + 1)
for (let l of ls) {
for (let r of rs) {
if (l + r <= distance) {
ans += 1
}
}
}
return [...ls, ...rs]
}
dfs(root)
return ans
}
894.所有可能的满二叉树
var allPossibleFBT = function (N) {
// 若只有一个节点
if (N == 1) return [new TreeNode(0)]
// 节点必须是偶数
if (N % 2 == 0) return []
let ans = []
let m = new Map()
for(let i = 1; i < N; i=i+2){
// 若为左边分配i个节点,则右边剩N-1-i,1为根节点
let j = N - 1 - i
const leftNodes = allPossibleFBT(i)
const rightNodes = allPossibleFBT(j)
for(let left of leftNodes){
for(let right of rightNodes){
const root = new TreeNode(0)
root.left = left
root.right = right
ans.push(root)
}
}
m.set(N, ans)
}
return m.get(N)
}
101.对称二叉树
迭代写法
var isSymmetric = function (root) {
// BFS
if (!root) return true
// 左右子树直接入队
let queue = [root.left, root.right]
while (queue.length) {
let queueLen = queue.length
for (let i = 0; i < queueLen; i = i + 2) {
let left = queue.shift()
let right = queue.shift()
// 当一边存在,另一边不存在,直接返回false
if ((!left && right) || (left && !right)) return false
if (left && right) {
// 如果左右都存在,但是值不相等,直接返回false
if (left.val !== right.val){
return false
}
// 值相等,就继续遍历下一层
queue.push(left.left, right.right)
queue.push(left.right, right.left)
}
}
}
return true
};
递归写法
var isSymmetric = function (root) {
// 递归
if (!root) return true
const check = function (left, right) {
// 递归终止条件,左右子树都是空,则对称
if (!left && !right) return true
// left和right都不为空
if(left && right){
// 则判断值是否相等,且左右子树是否相等
return left.val == right.val && check(left.left, right.right) && check(left.right, right.left)
}
return false
}
return check(root.left, root.right)
};
226.翻转二叉树
var invertTree = function(root) {
if(!root) return null
const reverse = function (root) {
// 递归终止条件,如果左右都为空,直接返回根节点
if(!root.left && !root.right){
return root
}
// 只要左右都不为空,就可以进行值交换
let temp = root.left
root.left = root.right
root.right = temp
// 如果存在,就可以交换
if(root.left) reverse(root.left)
if(root.right) reverse(root.right)
return root
}
return reverse(root)
}
543.二叉树的直径
var diameterOfBinaryTree = function (root) {
if (!root) return 0
let ans = 0
const dfs = function (root) {
// 递归终止条件
if (!root) return 0
const L = dfs(root.left)
const R = dfs(root.right)
// 因为最长直径不一定经过根节点,所以队每一个节点都计算左右子树的深度的和
ans = Math.max(ans, L + R)
// 返回左右子树深度的最大值
return Math.max(L, R) + 1
}
dfs(root)
return ans
}