二叉树

200 阅读5分钟

二叉树

  • 网站 esprima 返回语法树

空二叉树 没有节点 null 有一个根节点,左子树,右子树 二叉树结点左右子树为空 称为叶子结点

除最后两排 都有左右子树 称为完全二叉树

除最后一排 都有左右子树 称为满二叉树 每一层结点数量呈等比数列

计算二叉树结构 log2()

完全二叉树 叶子结点和总结点数量关系

二叉树指向null指针总比节点数量多一

n个结点 指针数量2n

二叉树的存储方式

对象

tnode = { val: 3, lfet: null, right: null }
tnode.lfet = { val: 5, lfet: null, right: null }
tnode.right = { val: 2, lfet: null, right: null }

数组

  • 用null补成完全二叉树 按序存储数组
  • 下标 n left 2n + 1 right 2n + 2 反推[2n - 1}/2 下取整
  • 缺点:非完全二叉树 null更多

将数组存储二叉树转换为链式表达

function createTreeNode(val) {
  return {
    val: val,
    left: null,
    right: null,
  }
}
//将用数组中根节点在i位置的树表达的二叉树转换为用链式表达的二叉树
function ary2tree(ary, i = 0) {
  if (ary[i] == null) {
    return null
  }//超出数组范围undefined == null 如果为null 他的子节点不会再展开
  var root = createTreeNode(ary[i])
  root.left = ary2tree(ary, 2 * i + 1)
  root.right = ary2tree(ary, 2 * i + 2)
  return root
}

####将使用链式表达法表达的二叉树转换为数组表达方式

function tree2ary(root, ary = [], idx = 0) {
  if (root == null) {
    //ary[idx] = null 可以填null 没有empty 但是空数组会返回[null]
    return []
  }
  ary[idx] = root.val
  tree2ary(root.left, ary, idx * 2 + 1)
  tree2ary(root.right, ary, idx * 2 + 2)
  return ary
}
tree =ary2tree([1,2,3,null,4,7,null,null,5,null,6,null,null])
tree2ary(tree)
//[1, 2, 3, empty, 4, 7, empty × 4, 6]
tree2ary(ary2tree([1,2,3,4,undefined,4,7,,,,,6]))
//[1, 2, 3, 4, empty, 4, 7, empty × 4, 6]
//ary = [1, , null, undefined]
//浏览器里 没有1下标 empty
var b = []
b[1] = 0
// b [empty,1]

将二叉树转换为紧凑型表示的数组

  • 只补必要的Null null延伸的null不补了
function treeToAry(root) {
  if (!root) {
    return []
  }
  var result = []
  var nextRow = [] //按顺序存储下一行结点
  var nodes = [root]
  while (nodes.length) {
    for (var i = 0; i < nodes.length; ++i) {
      if (nodes[i] == null) {
        result.push(null)
      } else {
        result.push(nodes[i].val) //nodes空 null.val 不行
        nextRow.push(nodes[i].left)
        nextRow.push(nodes[i].right)
      }
    }
    nodes = nextRow
    nextRow = []
  }
  return result
}

treeToAry(ary2tree([1,2,null,3,4]))
[1, 2, null, 3, 4, null, null, null, null]

//空间复杂度不到n
function treeToAry2(root) {
  if (!root) {
    return []
  }
  var result = []
  var i = 0
  nodes = [root]
  while (i < nodes.length) {
    var node = nodes[i++]
    if (nodes == null) {
      result.push(null)
    } else {
      result.push(node.val)
      nodes.push(node.left)
      nodes.push(node.right)
    }
  }
  return result
}
//空间复杂度2n
function treeToAry2(root) {
  if (!root) {
    return []
  }
  var result = []
  nodes = [root]
  while (nodes.length) {
    var node = nodes.shift() //数组从前往后删除
    if (nodes == null) {
      result.push(null)
    } else {
      result.push(node.val)
      nodes.push(node.left)
      nodes.push(node.right)
    }
  }
  return result
}
//按层
//直接处理下一行 按层次遍历
function treeToAry2(root) {
 if (!root) {
   return []
 }
 var result = [root.val]
 var nodes = [root]
 while (nodes.length) {
   var node = nodes.shift()
   if (node.left) {
     nodes.push(node.left)
     result.push(node.left.val)
   } else {
     result.push(null)
   }
   if (right.left) {
     nodes.push(node.right)
     result.push(node.right.val)
   } else {
     result.push(null)
   }
   while (result[result.length - 1] === null) {
     result.pop()
   }
   return result
 }

紧凑数组转化链式

  function aryToTree(ary) {
    if (ary.length == 0) {
      return null
    }
    var root = createTreeNode(ary[0])
    result = root
    var queue = [root]

    for (var i = 1; i < ary.length; i++) {
      var node = queue.shift()
      if (ary[i] !== null) {
        node.left = createTreeNode(ary[i])
        queue.push(node.left)
      }
      i++
      if (i >= ary.length) {
        break
      }
      if (ary[i] !== null) { //结点创建左右默认为null 不需要特意赋null
        node.right = createTreeNode(ary[i])
        queue.push(node.right)
      }

    }
    return root
  }
  aryToTree([1, 2, null, 3, 4, null, null, 5, 6, null, 7])

二叉树的遍历

  • 按层遍历
  • 前序 root l r 中序 l root r 后序 l r root
function preOrderTraverse(root) {
  if (root) {
    console.log(root.val)
    preOrderTraverse(root.left)
    preOrderTraverse(root.right)
  }
}
function inOrderTraverse(root) {
  if (root) {
    inOrderTraverse(root.left)
    console.log(root.val)
    inOrderTraverse(root.right)
  }
}
function postOrderTraverse(root) {
  if (root) {
    postOrderTraverse(root.left)
    postOrderTraverse(root.right)
    console.log(root.val)
  }
}


function preOrderTraverse(root, action) {
  if (root) {
    action(root.val)
    preOrderTraverse(root.left, action)
    preOrderTraverse(root.right, action)
  }
}
function preOrder(root) {
  var stack = []
  while (true) {
    while (root) {
      console.log(root.val)
      stack.push(root)
      root = root.left
    }
    if (!stack.length) {
      break
    }
    var node = stack.pop()
    root = node.right
  }
}
preOrder(aryToTree([1, 2, 3, 4, 5, 6, 7]))
function preOrder(root) {
  var stack = []
  while (true) {
    if (root) {
      console.log(root.val)
      stack.push(root)
      root = root.left
    } else if (stack.length) {
      root = stack.pop().right //stack非空 stack.pop一定非空
    } else {
      break
    }
  }
}
//空间复杂度 树的深度
自己搜索不用栈遍历二叉树
function inOrder(root) {
  var stack = []
  while (true) {
    if (root) {
      stack.push(root)
      root = root.left
    } else if (stack.length) {
      root = stack.pop()
      console.log(root.val)
      root = root.right
    } else {
      break
    }
  }
}
inOrder(aryToTree([1, 2, 3, 4, 5, 6, 7]))

回顾

//[-1, -2, -3, 1, 2, null, 3, 4, null, null, 5]
//根节点从1开始 子节点的数学关系乱了
//n为偏移量 上例n=3 (i-n)*2+1+n/2 (2i - n + 1)

function ary2tree(ary, i = 0, n = i) {
  if (ary[i] == null) {
    return null
  }
  var root = createTreeNode(ary[i])
  root.left = ary2tree(ary, 2 * i + 1 - n, n)
  root.right = ary2tree(ary, 2 * i + 2 - n, n)
  return root
}


function ary2tree5(ary, n) {
  return ary2tree(ary, n)
 
  function ary2tree(ary, i = 0) {
    if (ary[i] == null) {
      return null
    }
    var root = createTreeNode(ary[i])
    root.left = ary2tree(ary, 2 * i + 1 - n)
    root.right = ary2tree(ary, 2 * i + 2 - n)
    return root
  }
}

排序二叉树

  • BST Binary Search Tree
  • 左子树所有结点值都小于(或大于)根节点
  • 右子树所有结点值都大于(或小于)根节点
  • BST的中序遍历是升序的 反向中序遍历r c l 得到降序
  • 应用乱序数组排序
function createTreeNode(val) {
  return {
    val: val,
    left: null,
    right: null
  }
}

function insertIntoBST(bst, val) {
  if (bst == null) {
    return createTreeNode(val)
  }
  if (val < bst.val) {
    bst.left = insertIntoBST(bst.left, val) //生成的新节点挂在树上
  } else {
    bst.right = insertIntoBST(bst.right, val)
  }
  return bst
}

function bstSort(ary) {
  var tree = null
  for (var i = 0; i < ary.length; i++) {
    tree = insertIntoBST(tree, ary[i])
  }//这里可以用reduce

  var result = []
  inOrderTraverse(tree, val => {
    result.push(val)
  })
  return result
}

console.log(bstSort([8, 3, 4, 7, 2, 1, 5, 6, 9, 0]))
//时间复杂度n*logn 空间复杂度n 构造出来的bst占用的空间 理论最优
//平衡二叉树 所有结点左右子树深度差不超过1 最差n方
//非递归二叉搜索树
function insertIntoBST(bst, val) {
  if (bst === null) {
    return createTreeNode(val)
  }
  var p = bst
  while (true) {
    if (val < p.val) {
      if (p.left) {
        p = p.left
      } else {
        p.left = createTreeNode(val)
        break
      }
    } else {
      if (p.right) {
        p = p.right
      } else {
        p.right = createTreeNode(val)
        break
      }
    }
  }
  return bst
}
//空阿复杂度0(1)

判断数组是否升序

function isAscend(ary) {
  ary[-1] = -Infinity
  return ary.every((it, idx) => {
    return it >=  ary[idx - 1]
  })
}

Tips

  • 构建自平衡树 B + 红黑 AVL树

  • 插入排序 n方

    [2.1.4.7.5.8.9.0]

    [1.2.4.7.5.8.9.0]

    [1.2.4.5.7.8.9.0]

    [1.2.4.5.7.8.9.0]

    [1.2.4.5.7.8.0.9]

  • BST 高效查找 高效插入

  • 排序的稳定性: 排序前后相同元素相对位置是否发生变化

​ [2, 1, 4, 7, 4, 8, 3, 6, 4, 7]

  • 稳定:冒泡 BST 归并

    不稳定:快读排序 选择排序(无序部分最小元素放入有序部分的最后)