Leetcode 题解 - 树

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
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值