前端算法 - 二叉树

760 阅读4分钟

二维拓扑结构(图)

  • 二叉树 多叉树 都是基于拓扑结构生成的
// 拓扑结构
function Node (value) {
    this.value = value
    this.neighbor = []
}

var node1 = new Node(1)
var node2 = new Node(2)
var node3 = new Node(3)
var node4 = new Node(4)

node1.neighbor.push(node2, node3)
node2.neighbor.push(node1, node4)
node3.neighbor.push(node1, node4)
node4.neighbor.push(nod2, node3)

树形结构(有向无环图)

  • 树是图的一种
  • 树有一个根节点
  • 树没有环形结构(回路)
  • 度: 树的最多叉的节点有多少叉,就有多少度
  • 深度: 树最深有多少层,就是多少深度

二叉树

  • 树的度最多为二的树
  • 满二叉树

    1. 所有的叶子节点都在最底层
    2. 每个非叶子节点都有两个子节点
  • 完全二叉树

国内定义

  1. 叶子节点都在最后一层或者倒数第二层
  2. 叶子节点都向左聚拢

国际定义

  1. 叶子节点都在最后一层或者倒数第二层
  2. 如果有叶子节点,就必须有两个叶子节点

二叉树的遍历

  • 前序遍历(前根次序遍历)先打印当前的,再打印左边的,再打印右边的
  • 中序遍历(中根次序遍历)先打印左边的,先打印当前的,再打印右边的
  • 后序遍历(后根次序遍历)先打印左边的,再打印右边的,先打印当前的
// 构建二叉树
function Node(val) {
    this.value = val
    this.left = null
    this.right = null
}

var a = new Node('a')
var b = new Node('b')
var c = new Node('c')
var d = new Node('d')
var e = new Node('e')
var f = new Node('f')
var g = new Node('g')

a.left = c
a.right = b
b.left = e
b.right = f
c.left = g
  • 二叉树算法
// 前序遍历
function qianxu (node) {
    if (node === null) return
    console.log(node.val)
    qianxu(node.left)
    qianxu(node.right)
}

// 中序遍历
function zhongxu (node) {
    if (node === null) return
    qianxu(node.left)
    console.log(node.val)
    qianxu(node.right)
}

// 后序遍历
function houxu (node) {
    if (node === null) return
    qianxu(node.left)
    qianxu(node.right)
    console.log(node.val)
}

中序遍历 根节点的左边是左子树;

前序遍历 第一个是根节点;

后序遍历 最后一个是根节点

用中序遍历确认左右子树,前后序遍历确认根节点

// 根据前序后序 还原二叉树
var qian = ['a', 'b', 'e', 'f', 'c', 'g', ]
var zhong = ['b', 'e', 'f', 'a', 'c', 'g', ]
var num = 0
function huanyuan(qian, zhong) {
    if (qian == null || zhong == null || qian.length == 0 || zhong.length == 0 || qian.length != zhong.length) return null;
    var root = new Node(qian[0]) // 创建二叉树 根据前序找到根节点
    var zhongIdx = zhong.indexOf(root.val) // 在中序中找到根节点位置
    var qLeft = qian.slice(1, zhongIdx + 1) // 前序左子树
    var qRight = qian.slice(zhongIdx + 1, qian.length) // 前序右子树
    var zLeft = zhong.slice(0, zhongIdx) // 中序左子树
    var zRight = zhong.slice(zhongIdx + 1, zhong.length) // 中序右子树
    root.right = huanyuan(qLeft, zLeft)
    root.left = huanyuan(qRight, zRight)
    return root
}
var root = huanyuan(qian, zhong)

二叉树的搜索

  • 树的搜索 图的搜索 爬虫逻辑 搜索引擎爬虫逻辑
  • 深度优先搜索:更适合探索未知 往深处搜索
  • 广度优先搜索:更适合搜索局域 一层一层搜索

对于二叉树来说,深度优先搜索和前序遍历的顺序是一样的 广度优先搜索和中序遍历的顺序是一样的

// 深度优先搜索
/**
 *实现一颗二叉树 
 */
// 搜索 f 在不在这个二叉树中
function deepSearch (node, target) {
    if (node == null) return false
    if (node.value == target) return true
    // 向更深层递归搜索
    var left = deepSearch(node.left, target)
    var right = deepSearch(node.right, target)
    return left || right
}

/**
 * 二叉树的广度优先 每一层搜索完了再向下一层搜索
 */
function widthSearch(nodeList, target) {
    if (nodeList == null || nodeList.length == 0) return false
    var childList = []
    for (var i = 0; i < nodeList.length; i++) {
        if (nodeList[i] != null && nodeList[i].value == target) {
            return true
        } else {
            childList.push(nodeList[i].left)
            childList.push(nodeList[i].right)
        }
    }
    return widthSearch(childList, target)
}

/**
 * 二叉树比较
 * 左子树和右子树互换 不算 同一棵树的情况
 */
 function compareTree(tree1, tree2){
    if (tree1 == tree2) return true // 同一棵树
    if (tree1.value == tree2.value) {
        var left = compareTree(tree1.left, tree2.left)
        var right = compareTree(tree1.right, tree2.right)
        return left && right
    }
    return false
}

/**
 * 二叉树比较
 * 左子树和右子树互换 算 同一棵树的情况
 */
 function compareTree(tree1, tree2){
    if (tree1 == tree2) return true
    if (tree1.value == tree2.value) {
        return compareTree(tree1.left, tree2.left) && compareTree(tree1.right, tree2.right)
        || compareTree(tree1.left, tree2.right) && compareTree(tree1.right, tree2.left)
    }
    return false
}

二叉树diff算法

知道树结构新增了什么,删除了什么,修改了什么

/**
 * 二叉树的diff算法
 */
function diffList(tree1, tree2, diffLists = []) {
    if (tree1 == tree2) return diffLists
    // 修改 删除 新增 三种情况
    if (tree1.value != tree2.value) { // 修改
        diffLists.push({type: 'update', origin: tree1, now: tree2})
        // 修改需要继续向下递归,有可能子节点没有修改
        diffList(tree1.left, tree2.left)
        diffList(tree1.right, tree2.right)
    } else if (tree1 != null && tree2 == null) { // 删除
        diffLists.push({type: 'delete', origin: tree1, now: null})
    } else if (tree1 == null && tree2 != null) { // 添加
        diffLists.push({type: 'add', origin: null, now: tree2})
    } else { // 相同则遍历子节点
        diffList(tree1.left, tree2.left)
        diffList(tree1.right, tree2.right)
    }
    return diffLists
}