javascript数据结构 -- 树(三)

168 阅读2分钟

树(Tree)

本文作为树数据结构的第三篇,介绍二叉搜索树BST上除了删除方法之外的其它方法的实现思路,这些方法包括:节点插入,先序、中序、后序遍历,获取最大、最小key节点、查找特定key节点。

1. 插入节点

  • 插入一个新的节点的算法如下:
  • 判断树是否为空,如果是空树,则新插入的节点直接成为根节点
  • 如果不是空的,则尝试将新节点作为根节点的子节点插入;如果没有成功,则根节点应该指明该新节点下一步应该尝试插入到哪一个节点
  • 尝试插入的过程如下:
    • 判断新节点的key的值是否小于尝试性父节点
      • 如果小于,则进一步查看该节点的left是否空缺,如空缺则直接插入,否则指导新节点插入到其已有的左子节点中去;
      • 如果大于,则进一步查看该节点的right是否空缺,如空缺则直接插入,否则指导新节点插入到其已有的右子节点中去;
      • 如果等于,则插入非法,不处理。
    insert(key: number): void{
        // 根据key产生一个新节点
        const newNode = new _Node(key);
        // 判断根节点是不是存在
        if (!this.root) {
            this.root = newNode;
        } else {
            this.insertNode(this.root, newNode);
        }
    }

    insertNode(targetNode: _Node, newNode: _Node): void {
        if (newNode.key < targetNode.key) {
            if (!targetNode.left) {
                targetNode.left = newNode;
            } else {
                this.insertNode(targetNode.left, newNode);
            }
        } else {
            if (!targetNode.right) {
                targetNode.right = newNode;
            } else {
                this.insertNode(targetNode.right, newNode);
            }
        }
    }

2. 获取树中最大/最小key的节点

算法为:由于BST是有序结构,所以获取最大或者最小节点是比较简单的;事实上,树中最大的key节点就是右下角的节点;而最小key节点就是左下角的节点,因此,只要顺着root一直往左或者右往下走就可以得到最小值或者最大值了:

    min(): number {
        if(!this.root) return NaN;
        let pointerNode = this.root;
        while(pointerNode.left){
            pointerNode = pointerNode.left;
        }
        return pointerNode.key;
    }

    max(): number {        
        if(!this.root) return NaN;
        let pointerNode = this.root;
        while(pointerNode.right){
            pointerNode = pointerNode.right;
        }
        return pointerNode.key;
    }

3. 根据节点的key搜索对应的节点

算法为:从根节点开始, 将key和当前节点的key比较,依据比此节点key大的都在其右边,比此节点key小的都在左边的这个特点,通过二分法迅速找到对应节点:

    search(key: number): null | _Node {
        return this.searchNode(this.root, key);
    }

    searchNode(node: null | _Node, key: number): null | _Node {
        if(!node) return null;
        if (node.key > key) {
            // 此时应该搜索其左边
            return this.searchNode(node.left, key);
        } else if (node.key < key) {
            // 此时应该搜索其右边
            return this.searchNode(node.right, key);
        } else {
            // 找到的话就直接返回
            return node;
        }
    }

4. 先序遍历

  • 首先必须解释清楚的是【先序遍历】中的“先”的含义:先是针对树或者子树的根节点来说的,对一个根节点来说,有它自己、左子节点、右子节点三个对象,先序遍历的含义就是:先遍历自己,再遍历左子树,再遍历右子树:根->左->右;
  • 先序遍历的算法:从根节点开始,先遍历根,然后是左子树,最后是右子树
    preOrderTraverse(handler: Function): void {
        this.preOrderTraverseNode(this.root, handler);
    }

    preOrderTraverseNode(node: null | _Node, handler: Function): void {
        if(node){
            // 先访问根节点
            handler(node);
            // 然后是左子树
            this.preOrderTraverseNode(node.left, handler);
            // 最后是右子树
            this.preOrderTraverseNode(node.right, handler);
        }
    }

5. 中序遍历

  • 有了先序遍历的铺垫,中序遍历中的“中”,表示的无非是根节点要在中间被访问;
  • 中序遍历的算法:从根节点开始,先遍历左子树,然后是根节点,最后是右子树
    midOrderTraverse(handler: Function): void {
        this.midOrderTraverseNode(this.root, handler);
    }

    midOrderTraverseNode(node: null | _Node, handler: Function): void {
        if(node){
            // 先访问左子树
            this.midOrderTraverseNode(node.left, handler);
            // 然后是根节点
            handler(node);
            // 最后是右子树
            this.midOrderTraverseNode(node.right, handler);
        }
    }

6. 后序遍历

无需多言

    postOrderTraverse(handler: Function): void {
        this.postOrderTraverseNode(this.root, handler);
    }

    postOrderTraverseNode(node: null | _Node, handler: Function): void {
        if(node){
            // 先访问左子树
            this.postOrderTraverseNode(node.left, handler);
            // 然后是右子树
            this.postOrderTraverseNode(node.right, handler);
            // 最后是根节点
            handler(node);
        }
    }

注意

先序、中序、后序:说的都是根节点的位置,这三种遍历方式都是从root节点开始的,并且左一定在右之前!

7. 整体代码

class _Node {
    left: _Node | null = null;
    right: _Node | null = null;
    constructor (public key: number) {}
}

class _BST {
    root: _Node | null = null;
    insert(key: number): void{
        // 根据key产生一个新节点
        const newNode = new _Node(key);
        // 判断根节点是不是存在
        if (!this.root) {
            this.root = newNode;
        } else {
            this.insertNode(this.root, newNode);
        }
    }

    insertNode(targetNode: _Node, newNode: _Node): void {
        if (newNode.key < targetNode.key) {
            if (!targetNode.left) {
                targetNode.left = newNode;
            } else {
                this.insertNode(targetNode.left, newNode);
            }
        } else {
            if (!targetNode.right) {
                targetNode.right = newNode;
            } else {
                this.insertNode(targetNode.right, newNode);
            }
        }
    }

    preOrderTraverse(handler: Function): void {
        this.preOrderTraverseNode(this.root, handler);
    }

    preOrderTraverseNode(node: null | _Node, handler: Function): void {
        if(node){
            // 先访问根节点
            handler(node);
            // 然后是左子树
            this.preOrderTraverseNode(node.left, handler);
            // 最后是右子树
            this.preOrderTraverseNode(node.right, handler);
        }
    }

    midOrderTraverse(handler: Function): void {
        this.midOrderTraverseNode(this.root, handler);
    }

    midOrderTraverseNode(node: null | _Node, handler: Function): void {
        if(node){
            // 先访问左子树
            this.midOrderTraverseNode(node.left, handler);
            // 然后是根节点
            handler(node);
            // 最后是右子树
            this.midOrderTraverseNode(node.right, handler);
        }
    }

    postOrderTraverse(handler: Function): void {
        this.postOrderTraverseNode(this.root, handler);
    }

    postOrderTraverseNode(node: null | _Node, handler: Function): void {
        if(node){
            // 先访问左子树
            this.postOrderTraverseNode(node.left, handler);
            // 然后是右子树
            this.postOrderTraverseNode(node.right, handler);
            // 最后是根节点
            handler(node);
        }
    }

    min(): number {
        if(!this.root) return NaN;
        let pointerNode = this.root;
        while(pointerNode.left){
            pointerNode = pointerNode.left;
        }
        return pointerNode.key;
    }

    max(): number {        
        if(!this.root) return NaN;
        let pointerNode = this.root;
        while(pointerNode.right){
            pointerNode = pointerNode.right;
        }
        return pointerNode.key;
    }

    search(key: number): null | _Node {
        return this.searchNode(this.root, key);
    }

    searchNode(node: null | _Node, key: number): null | _Node {
        if(!node) return null;
        if (node.key > key) {
            // 此时应该搜索其左边
            return this.searchNode(node.left, key);
        } else if (node.key < key) {
            // 此时应该搜索其右边
            return this.searchNode(node.right, key);
        } else {
            // 找到的话就直接返回
            return node;
        }
    }

    // 非常难搞
    remove(key: number): boolean {return false;}
}