3.二叉树

77 阅读3分钟

1.递归序:在递归过程中每个节点都有三次访问的机会(用不用取决于你)

function f(head) {
  if(!head) return null
  
  // 先序访问head
  f(head.left)
  // 中序访问head
  f(head.right)
  // 后序访问head
}
// 递归序 1 2 4 4 4 2 5 5 5 2 1 3 6 6 6 3 7 7 7 3 1
// 先序   1 2 4 5 3 6 7 (第一次出现打印)
// 中序   4 2 5 1 6 3 7 (第二次出现打印)
// 后序   4 5 2 6 7 3 1 (第三次出现打印)
  1. 非递归先序:根左右 ==求导==> 根右左
function f(head) {
  if(!head) return 

  let stack = []
  let cur = head 
  stack.push(cur)
  while (stack.length) {
    cur = stack.pop()
    console.log(cur.value)
    if(cur.right) stack.push(cur.right)
    if(cur.left) stack.push(cur.left)
  }
}

3.非递归后序:左右根 ==颠倒==> 根右左 ==求导==> 根左右

function f(head) {
  if(!head) return 
  let stack1 = []
  let stack2 = []
  let cur = head
  stack1.push(cur)
  while (stack1.length) {
    let cur = stack1.pop()
    stack2.push(cur) 
    if(cur.left) stack1.push(cur.left)
    if(cur.right) stack1.push(cur.right)
  }
  while (stack2.length) {
    console.log(stack2.pop().value)
      }
}

4.非递归中序:左根右 ==左边界分解==> 左根(左根(左根(左根右)))

function f(head) {
  if(!head) return

  let stack = []
  let cur = head
  while (stack.length||cur) {
    if(cur){
      stack.push(cur)
      cur = cur.left
    }else{
     cur = stack.pop()
     console.log(cur.value)
     cur = cur.right 
    }
  } 
}

5.求树的最大宽度(BFS),使用队列Queue

// 先来介绍宽度优先遍历BFS
function f(head){
  if(!head) return 
  
  let queue = [head]
  let leaf = false // 叶节点开关
  while(queue.length){
    head = queue.shift()
    console.log(head.value)
    
    head.left && queue.push(head.left)
    head.right && queue.push(head.right)
    
    if(!head.left||!head.right) leaf = true 
  }
}
// 1.使用hash表
function f(head) {
  if(!head) return 0

  let queue = [head]
  let levelMap = new Map() // 节点所在层map
  levelMap.set(head,0)

  let level = 0 // 迭代层次
  let count = 0
  let max = 0
  while (queue.length) {
    head=queue.shift()
    let curLevel=levelMap.get(head)
    if(curLevel===level){
      count++ // 当前层
    } 
    else{ // 结算上一层,并置count为1,level++
      max = Math.max(max,count)
      level++
      count=1
    }
    if(head.left){
      queue.push(head.left)
      levelMap.set(head.left,level+1)
    }
    if(head.right){
      queue.push(head.right)
      levelMap.set(head.right,level+1)
    }
  }
  max = Math.max(max,count) // 最后再结算一次
  return max
}

// 2.不用hash表
function f(head) {
  if(!head) return 0
  let queue = [head]
  let curEnd = head // 当前层最后节点
  let nextEnd = null // 下一次最后节点
  let max = 0
  let count = 0
  while (queue.length) {
    head = queue.shift()
    count++
    if(head.left) {
      nextEnd = head.left
      queue.push(head.left)
    }
    if(head.right){
      nextEnd = head.right
      queue.push(head.right)
    }
    if(head===curEnd) {
      max = Math.max(max,count)
      count=0
      curEnd = nextEnd
    }
  }
  return max
}

6.判断搜索二叉树:右子树任意节点值>=根值>=左子树的任意节点值,使用中序

// 递归
function f(head) {
  let preVal = -Infinity
  return (function isBst(node) {
    if(!node) return true
  
    if(!isBst(node.left)) return false // 左树判断

    if(node.value<preVal) return false // 当前节点判断
    preVal=node.value
    
    return isBst(node.right) // 右树判断
  })(head)
}

// 简单版
function f(head){
  let stack = []
  
  // 递归
  (function f2(node,stack){
    if(!node) return
    f2(node.left,stack)
    stack.push(node.value)
    f2(node.right,stack)
  })(head,stack)
  
  for(let i=stack.length-1;i>0;i--) if(stack[i]<stack[i-1]) return false
  return true
  
  // 非递归
  let preVal = -Infinity
  while(stack.length||head){
    if(head){
      stack.push(head)
      head = head.left
    }else{
      head = stack.pop()
      
      if(head.value<preVal) return false
      else preVal=head.value
      
      head=head.right
    }
  }
  return true
}

7.判断完全二叉树(BFS)

// 有右无左返false
// 当前节点为叶节点,后续节点不为叶节点返false
function f(head) {
  if(!head) return true
  
  let leaf = false // 叶节点开关
  let l=r=null
  let stack = [head]
  while(stack.length){
    head = stack.shift()
    l=head.left
    r=head.right
    if((!l&&r) || (leaf&&(l||r))) return false
    if(!l||!r) leaf=true
    
    l&&stack.push(l)
    r&&stack.push(r)
  }
  return true
}

10.二叉树套路:可以用于解决树型DP问题

模板

function f(head){
  return (function(node){
    if(!node) return null
    let l = arguments.callee(node.left)
    let r = arguments.callee(node.right)
    
    let key = F(l, r, node)
    // ...
    let keyN = Fn(l, r, node)
    return { key, ..., keyN }
  })(head).key
}
// 1.判断是否是搜索二叉树
// 对于当前节点来说:左右子树必须是搜索二叉树且左子树最大节点值<=当前节点,右子树最小节点值>=当前节点
function f(node) {
  return (function (node) {
    if (!node) return null
    let l = arguments.callee(node.left)
    let r = arguments.callee(node.right)

    let max = r ? r.max : node.value
    let min = l ? l.min : node.value
    let isBst = (l ? l.isBst && l.value <= node.value : true) && (r ? r.isBst && r.value >= node.value : true)

    return { isBst, max, min }
  })(node).isBst
}

// 2.判断是否是满二叉树:nodes = 2^h - 1 = 1<<h -1
function f(node) {
  let root = (function (node) {
    if (!node) return null
    let l = arguments.callee(node.left)
    let r = arguments.callee(node.right)

    let nodes = (l ? l.nodes : 0) + (r ? r.nodes : 0) + 1
    let height = Math.max(l ? l.height : 0, r ? r.height : 0) + 1
    
    return { nodes, height }
  })(node)

  return root.nodes === (1 << root.height - 1)
}
// 3.判断平衡二叉树,左右平衡且高度差小于等于1
function f(node) {
  return (function (node) {
    if (!node) return null
    let l = arguments.callee(node.left)
    let r = arguments.callee(node.right)

    let height = Math.max(l ? l.height : 0, r ? r.height : 0) + 1
    let isBalance = l.isBalance && r.isBalance && Math.abs(l.height - r.height) <= 1

    return { isBalance, height }
  })(node).isBalance
}

11.求n1和n2的最低公共祖先

// 遍历所有节点,使用map记录父节点,然后通过map实现自低向上遍历,将遍历过的节点放入set
function f(h,n1,n2) {
  let fMap = new Map()
  fMap.set(head,null)
  
  ;(function (node) {
    if(node.left){
      fMap.set(node.left,node)
      arguments.callee(node.left)
    }
    if(node.right){
      fMap.set(node.right,node)
      arguments.callee(node.right)
    }
  })(h)
  
  let set = new Set()
  while (fMap.get(n1)) {
    set.add(n1)
    n1 = fMap.get(n1)
  }
  while (fmap.get(n2)) {
    if(set.has(n2)) return n2
    set.add(n2)
    n2 = fMap.get(n2)
  }
  return h // 尽头是head
}

// 自上而下
function f(node,n1,n2) {
  if(!node||n1===node||n2===node) return node // 临界点

  let l = arguments.callee(node.left,n1,n2)
  let r = arguments.callee(node.right,n1,n2)

  if(l&&r) return node // 左右有值返自身
  return l||r // 返回有值的节点
}
// 序列化
function f1(node) { // 先序
  if(!node) return '#-'
  return `${node.value}-${arguments.callee(node.left)+arguments.callee(node.right)}`
}

// 反序列化
function f2(str) {
  let arr = str.split('-')
  arr.pop()
  process(arr)
}
function process(arr) {
  let str = arr.shift()
  if(str==='#') return null

  let node = {value:+str} // 先序
  node.left = arguments.callee(arr)
  node.right = arguments.callee(arr)

  return node
}