数据结构篇(8) 二叉树的一些基本操作实现

/**
 * parent: 双亲节点
 * left: 左子节点
 * right: 右子节点
 */
interface TreeNode {
    left: TreeNode | null,
    right: TreeNode | null,
    data: any,
    count: number
}

class TreeNode {
    constructor(data: any, left: TreeNode | null, right: TreeNode | null) {
        this.data = data;
        this.left = left;
        this.right = right;
        this.count = 1;
    }
}

//二叉排序树
interface BsTree {
    root: TreeNode | null
    //删除一个节点
    _removeNode(node: TreeNode, data: any): void
    //删除给定的数据节点
    remove(data: any): void
    //向二叉树中插入节点
    insert(data: any): void
    //寻找给定数据的节点
    find(data: any): TreeNode | null
    //获得最小值的节点
    getMinNode(node: TreeNode): TreeNode | null
    //获得最大值的节点
    getMaxNode(node: TreeNode): TreeNode | null
    display(): void
    // 获得二叉树中节点个数
    getNodeNumber(node: TreeNode): number

    // 获得二叉树中叶子节点的个数
    getLeafNodeNumber(node: TreeNode): number

    // 获取二叉树的深度
    getTreeDepth(node: TreeNode): number

    // 计算给定层数节点的个数
    getLevelNodeNumber(level: any, node: TreeNode): number

    // 判断二叉树是不是完全二叉树
    isCompleteTree(node: TreeNode): boolean

    // 求二叉树的镜像
    invertTree(node: TreeNode): void
}

class BsTree {
    root: TreeNode | null = null

    /**
     * 
     * @param node 要删除的节点
     * @param data 要删除值
     * 递归删除节点
     * 待删除的节点是叶子节点。
     * 待删除的节点没有左子节点,或者没有右子节点。
     * 待删除的节点的左右子节点均存在。
     * 当待删除的节点时叶子节点时,这种情况比较简单,直接将待删除的节点置空返回即可。
     * 当待删除的节点没有左子节点时,返回该节点的右孩子节点,并删除该节点。待删除节点没有右节点时类似处理。
     * 比较麻烦的是最后一种情况,待删除的节点的左右子节点均存在时,可以有两种做法:要么查找待删除节点左子树上的最大值,要么查找其右子树上的最小值。
     * 这里使用查找其右子树上的最小值的方法。在找到待删除节点的右子树上的最小值后,创建一个临时节点,将临时节点上的值复制到待删除节点,然后再删除临时节点。
     */
    _removeNode(node: TreeNode, data: any): TreeNode | null {
        if (node == null) {
            return null;
        }
        if (data == node.data) {
            if (node.left == null && node.right == null) {
                return null;
            }
            //没有左节点的节点
            if (node.left == null) return node.right;
            //没有右节点的节点
            if (node.right == null) return node.left;
            //有两个节点的节点
            /*  
               做法:
                  找到待删除节点的右子树上的最小值创建一个临时节点。
                  将临时节点上的值复制到待删除节点,然后再删除临时节点
          */
            // 寻找右子树上的最小值
            let tmpNode = this.getMinNode(node.right);
            if (tmpNode) {
                node.data = tmpNode.data;
                node.right = this._removeNode(node.right, tmpNode.data);
                return node;
            }
        } else if (data < node.data && node.left) {  // 待删除节点在左子树上
            node.left = this._removeNode(node.left, data);
            return node;
        } else {  // 待删除节点在右子树上
            if (node.right) {
                node.right = this._removeNode(node.right, data);
                return node;
            }
        }
        return null
    }

    /**
     * 
     * @param data 要删除的节点值
     */
    //删除给定的数据节点
    remove(data: any): void {
        if (this.root) {
            console.log(this._removeNode(this.root, data));
        }
    }
    /**
     * 如果在插入时,root节点为空,则直接将新节点赋给root节点即可。
    * 如果新的节点值小于当前节点值,说明待插入的位置应在在当前节点的左子树上,那么在大于时,就应该在当前节点的右子树上。进而更新当前节点所指向的节点,直到当前节点为空时,说明找到了正确的插入位置。
    */
    insert(data: any): void {
        let newNode = new TreeNode(data, null, null);
        let parentNode = null;
        if (this.root === null) {
            this.root = newNode;
        } else {
            let currNode: any = this.root;
            while (true) {
                parentNode = currNode;
                if (newNode.data > currNode.data) {
                    currNode = currNode.right;
                    if (!currNode) {
                        parentNode.right = newNode;
                        break;
                    }
                } else if (newNode.data < currNode.data) {
                    currNode = currNode.left;
                    if (!currNode) {
                        parentNode.left = newNode;
                        break;
                    }
                } else if (currNode.data === newNode.data) {
                    currNode.count++;
                    break;
                }
            }
        }
    }

    //寻找给定数据的节点
    find(data: any): TreeNode | null {
        if (!this.root) {
            return null;
        }
        let currNode: TreeNode = this.root;
        while (currNode) {
            if (data > currNode.data && currNode.right) {
                currNode = currNode.right;
            } else if (data < currNode.data && currNode.left) {
                currNode = currNode.left;
            } else {
                return currNode;
            }
        }
        return null;
    }
    //获得最小值的节点
    getMinNode(node: TreeNode | null = this.root): TreeNode | null {
        let currNode = node;
        while (currNode?.left) {
            currNode = currNode.left;
        }

        return currNode;
    }
    //获得最大值的节点
    getMaxNode(node: TreeNode | null = this.root): TreeNode | null {
        let currNode = node;
        while (currNode?.right) {
            currNode = currNode.right;
        }
        return currNode;
    }

    preOrderRec(node: any = this.root) {
        let result = '';
        if (!(node == null)) {
            result += `${node.data} `;
            result += this.preOrderRec(node.left);
            result += this.preOrderRec(node.right);
        }
        return result;
    }

    // 前序遍历非递归方法
    preOrderNonRec(node: any = this.root) {
        let stack: Array<any> = [];
        let result = '';
        while (node || stack.length) {
            if (node) {
                result += `${node.data} `;
                stack.push(node);
                node = node.left;
            } else {
                node = stack.pop();
                node = node.right;
            }
        }
        return result;
    }

    inorderRec(node: any = this.root) {
        let result = '';
        if (!(node == null)) {
            result += this.inorderRec(node.left);
            result += `${node.data} `;
            result += this.inorderRec(node.right);
        }
        return result;
    }

    //中序遍历非递归算法
    inorderNonRec(node: any = this.root) {
        let result = '';
        let stack: Array<any> = [];
        while (node || stack.length) {
            if (node) {
                stack.push(node);
                node = node.left
            } else {
                node = stack.pop();
                result += `${node.data} `;
                node = node.right;
            }
        }
        return result;
    }

    //后续遍历
    postorderRec(node: any = this.root) {
        let result = '';
        if (!(node == null)) {
            result += this.postorderRec(node.left);
            result += this.postorderRec(node.right);
            result += `${node.data} `
        }
        return result;
    }

    getNodeNumber(node = this.root) {
        // 统计二叉树中结点个数的算法 (先根遍历)
        let count = 0;
        if (node) {
            count++;
            count += this.getNodeNumber(node.left);
            count += this.getNodeNumber(node.right)
        }
        return count;
    }

    getnorNodeNumber(node: any = this.root) {
        let queue = [];
        let count = 0;
        queue.push(node);
        while (queue.length) {
            count++;
            let node = queue.shift();
            if (node.left) {
                queue.push(node.left);
            }
            if (node.right) {
                queue.push(node.right);
            }
        }
        return count;
    }

    //递归求叶子节点数量
    getLeafNodeNumber(node: TreeNode | null = this.root): number {
        let count = 0;
        if (node) {
            if (node.right == null && node.left == null) {
                count++;
            }
            count += this.getNodeNumber(node.left);
            count += this.getNodeNumber(node.right);
        }
        return count;
    }

    //非递归求叶子节点数量
    getLeafnorNodeNumber(node: any = this.root): number {
        let count = 0;
        let queue = [];
        if (node) {
            queue.push(node);
            while (queue.length) {
                node = queue.shift();
                if (node.left) {
                    queue.push(node.left);
                }
                if (node.right) {
                    queue.push(node.right);
                }
                if (!node.right && !node.left) {
                    count++;
                }
            }
        }
        return count;
    }

    //获得二叉树的深度 
    getTreeDepth(node: any = this.root): number {
        if (!node) {
            return 0;
        }

        let leftMax = this.getTreeDepth(node.left);
        let rightMax = this.getTreeDepth(node.right);
        if (leftMax > rightMax) {
            return leftMax + 1;
        } else {
            return rightMax + 1;
        }
    }

    //获得规定层数的节点个数(层序遍历法)
    getLevelNodeNumber(level: number, node: any = this.root) {
        if (node) {
            let queue = [];
            let depth = 1;
            queue.push(node);
            if (level === queue.length) return queue.length;
            while (true) {
                let size = queue.length;
                if(size == 0) {
                    break;
                }
                while (size) {
                    node = queue.shift();//当前队列全部出栈后,保存的就是下一层节点
                    if (node.left) {
                        queue.push(node.left);
                    }
                    if (node.right) {
                        queue.push(node.right);
                    }
                    size--;
                }
                depth++;
                if (depth === level) {
                    return queue.length;
                }
            }
        }
    }

    //判断二叉树是不是完全二叉树 如果层次遍历时遇到一个空节点后,再向后的遍历的时候依然还有节点,则这个二叉树肯定不是完全二叉树。
    isCompleteTree(node:any = this.root):boolean {
        let queue = [];
        let flag = false;
        queue.push(node);
        while(queue.length) {
            node = queue.shift();
            if(node) {
                if(flag)  return false;
                queue.push(node.left);
                queue.push(node.right);
            } else {
                flag = true;
            }
        }
        return true;
    }

    //如果一个节点只有右子节点,则不是完全二叉树。当遍历到一个不是叶子节点的节点时,如果其前面的节点没有右孩子节点,则不是完全二叉树。
    isCompleteTree2(node:any = this.root):boolean {
        let queue = [];
        let noright = false;
        queue.push(node);
        while(queue.length) {
            node = queue.shift();
            if(!node.left&&node.right) {
                return false;
            }
            if((node.left||node.right) && noright) {
                return true;
            }
            if(node.left) {
                queue.push(node.left);
            }
            if(node.right) {
                queue.push(node.right);
            } else {
                noright = true;
            }
        }
        return true;
    }

    //镜像二叉树
    invertTree(node = this.root) {
        // 递归的方法
        if (!node) return;

        // 交换当前节点的左右子树
        let tmpNode = node.left;
        node.left = node.right;
        node.right = tmpNode;
        
        this.invertTree(node.left);
        this.invertTree(node.right);
    }
}

/**
    * @param {Array} preArr 先序序列
    * @param {Array} inArr 中序序列
    * @param {Number} pBeg 先序序列的第一个下标
    * @param {Number} pEnd 先序序列的最后一个下标
    * @param {Number} iBeg 中序序列的第一个下标
    * @param {Number} iEnd 中序序列的最后一个下标
    */


let myTree = new BsTree();

let tree2 = new BsTree();
function preInCreate(preArr: Array<Number>, inArr: Array<number>, pBeg: number, pEnd: number, iBeg: number, iEnd: number): TreeNode {
    let node = new TreeNode(preArr[pBeg], null, null);
    let lLen = 0, rLen = 0;
    let splitIdx = -1;
    if (tree2.root == null) {
        tree2.root = node;
    }
    for (let i: number = iBeg; i <= iEnd; i++) {
        if (inArr[i] === node.data) {
            splitIdx = i;
            break;
        }
    }
    if (splitIdx > -1) {
        lLen = splitIdx - iBeg;
        rLen = iEnd - splitIdx;
    }

    if (lLen) {
        node.left = preInCreate(preArr, inArr, pBeg + 1, pBeg + lLen, iBeg, iBeg + lLen - 1);
    } else {
        node.left = null;
    }
    if (rLen) { // 递归建立右子树
        node.right = preInCreate(preArr, inArr, pEnd - rLen + 1, pEnd, iEnd - rLen + 1, iEnd);
    } else {
        node.right = null;
    }

    return node;
}
let preOrder = [20, 13, 7, 9, 15, 14, 42, 22, 21, 24, 57];
let inorder = [7, 9, 13, 14, 15, 20, 21, 22, 24, 42, 57];
let inLstIdx = inorder.length - 1;
let preLstIdx = preOrder.length - 1;
preInCreate(preOrder, inorder, 0, preLstIdx, 0, inLstIdx);

myTree.insert(20);
myTree.insert(13);
myTree.insert(7);
myTree.insert(9);
myTree.insert(15);
myTree.insert(14);
myTree.insert(42);
myTree.insert(22);
myTree.insert(21);
myTree.insert(24);
myTree.insert(57);

console.log(myTree.getNodeNumber());
console.log(myTree.getTreeDepth())
console.log(myTree.getLevelNodeNumber(3))
// console.log(myTree.postorderRec())

相关文章

这篇文章主要介绍“基于nodejs的ssh2怎么实现自动化部署”的...
本文小编为大家详细介绍“nodejs怎么实现目录不存在自动创建...
这篇“如何把nodejs数据传到前端”文章的知识点大部分人都不...
本文小编为大家详细介绍“nodejs如何实现定时删除文件”,内...
这篇文章主要讲解了“nodejs安装模块卡住不动怎么解决”,文...
今天小编给大家分享一下如何检测nodejs有没有安装成功的相关...