树与森林--JavaScript版

689 阅读3分钟

树和森林

树和森林的定义

  • 树:和二叉树的定义类似,不同在于树中的节点可以有0个或者多个后继节点
  • 森林:多棵不相交的树的集合

树的存储

  • 双亲表示法
    // 节点
    class Node {
        constructor (data, parentIndex) {
            this.data = data || null
            this.parentIndex = parentIndex || -1
        }
    }
    
    // 使用连续存储空间存储树中的各个节点
    class Tree {
        constructor (length) {
            this.data = new Array(length)
        }
        addNode (node) {
            this.data.push(node)
        }
    }

    const tree = new Tree(6)
    tree.addNode(new Node('A', -1))
    tree.addNode(new Node('B', 0))
    tree.addNode(new Node('C', 0))
    tree.addNode(new Node('D', 1))
    tree.addNode(new Node('E', 1))
    tree.addNode(new Node('F', 1))

双亲表示法查找双亲节点只要常数时间,但是如果查找节点的孩子节点则必须遍历整个树的节点,效率低下

  • 孩子表示法
    class ChainNode {
        constructor (childIndex, next) {
            this.childIndex = childIndex
            this.next = next || null
        }
    }
    class Node {
        constructor (data, firstChildIndex) {
            this.data = data
            this.firstChildIndex = firstChildIndex || null
        }
    }
    class Tree {
        constructor (length) {
            this.data = new Array(length)
        }
        addNode (node) {
            this.add.push(node)
        }
    }

    const tree = new Tree(6)
    tree.addNode(new Node('A'))
    tree.addNode(new Node('B'))
    tree.addNode(new Node('C'))
    tree.addNode(new Node('D'))
    tree.addNode(new Node('E'))
    tree.addNode(new Node('F'))

    const a = new ChainNode(1, b)
    const b = new ChainNode(2)
    tree.data[0].firstChildIndex = a

    const d = new ChainNode(3, e)
    const e = new ChainNode(4, f)
    const f = new ChainNode(5)
    tree.data[1].firstChildIndex = d

孩子表示法查找节点的孩子节点是O(1)的,但查找节点的双亲节点却效率较低

  • 双亲孩子表示法

顾名思义就是结合上面两种的优点完成树的存储,能够快速的查找出节点的孩子节点和双亲节点

    class ChainNode {
        constructor (childIndex, next) {
            this.childIndex = childIndex
            this.next = next || null
        }
    }
    class Node {
        constructor (data, parentIndex, firstChildIndex) {
            this.data = data
            this.parentIndex = parentIndex || -1 // 指向双亲节点的下标
            this.firstChildIndex = firstChildIndex || null // 指第一孩子的下标
        }
    }
    class Tree {
        constructor (length) {
            this.data = new Array(length)
        }
        addNode (node) {
            this.add.push(node)
        }
    }

    const tree = new Tree(6)
    tree.addNode(new Node('A', -1))
    tree.addNode(new Node('B', 0))
    tree.addNode(new Node('C', 0))
    tree.addNode(new Node('D', 1))
    tree.addNode(new Node('E', 1))
    tree.addNode(new Node('F', 1))

    const a = new ChainNode(1, b)
    const b = new ChainNode(2)
    tree.data[0].firstChildIndex = a

    const d = new ChainNode(3, e)
    const e = new ChainNode(4, f)
    const f = new ChainNode(5)
    tree.data[1].firstChildIndex = d

该存储方法可以快速的定位到节点的孩子和双亲节点

  • 孩子兄弟表示法
    class Node {
        construcrot (data, firstChild, brother) {
            this.data = data
            this.firstChild = firstChild || null
            this.brother = brother || null
        }
    }

    const a = new Node('A', b, null)
    const b = new Node('B', d, c)
    const c = new Node('C', null, null)
    const d = new Node('D', null, e)
    const e = new Node('E', null, f)
    const f = new Node('F', null, null)

该表示法采用链式存储了树

森林的存储

  • 双亲表示法
    // 和树的存储一样,把森林的所有节点放入一个一维数组,只是多了几个根节点而已(指向双亲的指针为-1)
    // 可以再使用一个一维数组保存各个树的根节点的索引下标
  • 孩子表示法
    // 类似与 森林的双亲表示法
  • 孩子兄弟表示法
    // 把森林中的第一棵树用 孩子兄弟表示法保存后,其余的树则可以当作 第一棵树的兄弟节点进行保存

树和森林与二叉树的转换

  • 树和二叉树的转换

由下面的转换规则可知:
树转化成二叉树后,它的根节点一定没有右子树
根节点没有右子树的二叉树转成二叉树后,一定是一个树(如果有右子树,则会转化成森林)

  • 森林和二叉树的转换

    森林和二叉树的转化和 树与二叉树的转化是一摸一样的(也就是 加线,去线,旋转)

树与森林的遍历

  • 树的遍历

    先序遍历
    后序遍历
    层次遍历

  • 森林的遍历

    先序遍历
    中序遍历

树和森林如果使用 二叉链表进行存储的话,遍历等操作可以使用二叉树的方法
(树和森林的链式存储和转化成二叉树后的结构一样,只是具体含义不同)