实现二叉搜索树

二叉树的操作有

  • 插入节点
  • 查找节点
  • 删除节点(复杂)
  • 前序遍历(中左右)
  • 中序遍历(左中右)
  • 后序遍历(左右中)
  • 广度优先遍历(层次遍历)
  • 获取最小值节点
  • 获取最大值节点
  • 获取树的深度
class BstNode {
    constructor(val, left = null, right = null) {
        this.val = val
        this.left = left
        this.right = right
    }
    print() {
        console.log(this.val);
    }
}

// 二叉搜索树,左节点 < 父节点 < 右节点
class BinarySearchTree {
    constructor() {
        this.root = null // 根节点
    }

    // 插入节点
    insert(val) {
        const node = new BstNode(val)
        if (!this.root) {
            this.root = node
            return
        }
        // 遍历,通过比较val大小找到合适的位置插入新节点
        let cur = this.root
        while (true) {
            // 比父节点小时,新节点位置一定在父节点左侧
            // 比父节点大时,新节点位置一定在父节点右侧
            if (val < cur.val) {
                if (cur.left === null) {
                    cur.left = node
                    break
                }
                cur = cur.left // 继续往左侧找
            } else {
                if (cur.right === null) {
                    cur.right = node
                    break
                }
                cur = cur.right // 继续往右侧找
            }
        }
    }
    // 查找节点
    find(val) {
        let cur = this.root
        while (cur) {
            if (val === cur.val) {
                return cur
            } else if (val < cur.val) {
                cur = cur.left
            } else if (val > cur.val) {
                cur = cur.right
            }
        }
        return null
    }
    // 查找节点,递归实现
    find2(val, node) {
        if (!node) {
            return null
        }
        if (val === node.val) {
            return node
        } else if (val < node.val) {
            return this.find2(val, node.left)
        } else if (val > node.val) {
            return this.find2(val, node.right)
        }
    }
    // 删除节点
    // 3种情况
    // 第1种:待删除节点为叶子节点,只需把父节点指向该节点的链指向null
    // 第2种:待删除节点左节点或右节点为null,只需把父节点指向该节点的链指向剩下的左子树或右子树
    // 第3种:待删除节点的左右节点都存在
    // (1)找到待删除节点 左子树的最大值节点 或 右子树的最小值节点;以右子树最小值节点为例
    // (2)将最小值节点的val赋值给当前节点
    // (3)然后删除最小值节点(采用递归)
    // (4)删除最小值节点后,右子树根节点可能发生变化,当前节点应重新连接新的右子树根节点
    remove(node, val) {
        if (!node) {
            return null
        }
        // 先寻找待删除节点
        if (val < node.val) {
            node.left = this.remove(node.left, val)
            return node
        } else if (val > node.val) {
            node.right = this.remove(node.right, val)
            return node
        } else if (val === node.val) { // 找到待删除的节点了
            // 如果是叶子节点
            if (node.left === null && node.right === null) {
                return null
            } else if (node.right === null) { // 只含左节点
                return node.left
            } else if (node.left === null) { // 只含右节点
                return node.right
            } else if (node.left !== null && node.right !== null) { // 含两个子节点
                // 找到右子树的最小值
                let tempNode = this.getMin(node.right)
                // 将最小值节点
                node.val = tempNode.val
                // 然后删除这个最小值节点
                node.right = this.remove(node.right, tempNode.val)
                return node
            }
        }
    }

    // 前序遍历,中左右
    preOrder(node) {
        if (node) {
            node.print() // 中
            this.preOrder(node.left) // 左
            this.preOrder(node.right) // 右
        }
    }
    // 中序遍历,左中右
    inOrder(node) {
        if (node) {
            this.inOrder(node.left) // 左
            node.print() // 中
            this.inOrder(node.right) // 右
        }
    }
    // 后序遍历,左右中
    postOrder(node) {
        if (node) {
            this.postOrder(node.left) // 左
            this.postOrder(node.right) // 右
            node.print() // 中
        }
    }

    // 广度优先遍历,利用队列实现
    levelOrder() {
        if (!this.root) {
            return
        }
        let queue = []
        let res = []

        queue.push(this.root)
        while (queue.length) {
            // 从队列头取出节点
            let node = queue.shift()
            res.push(node.val)
            // 从队尾插入子节点
            if (node.left) {
                queue.push(node.left)
            }
            if (node.right) {
                queue.push(node.right)
            }
        }
        return res
    }
    levelOrder2() {
        let queue = []
        let res = []
        queue.push(this.root)
        while (queue.length) {
            let len = queue.length // 当前层的节点数
            // 循环把当前层的节点拿出来,再把左节点和右节点插入队列
            for (let i = 0; i < len; i++) {
                let node = queue.shift()
                res.push(node.val)
                if (node.left) {
                    queue.push(node.left)
                }
                if (node.right) {
                    queue.push(node.right)
                }
            }
        }
        return res
    }
    // 获取最小值,在左子树最后一层最左边的节点
    getMin(node) {
        node = node || this.root
        let curr = node
        while (curr.left !== null) {
            curr = curr.left
        }
        return curr
    }
    // 用递归实现
    getMin2(node) {
        node = node || this.root
        if (node.left === null) {
            return node
        } else {
            return this.getMin2(node.left)
        }
    }
    // 获取最大值,在右子树最后一层最右边的节点
    getMax(node) {
        node = node || this.root
        let curr = node
        while (curr) {
            if (curr.right === null) {
                return curr
            }
            curr = curr.right
        }
    }
    // 树的深度
    getDeep(node, deep) {
        deep = deep || 0
        if (node === null) {
            return deep
        }
        deep++
        let leftDeep = this.getDeep(node.left, deep) // 左子树深度
        let rightDeep = this.getDeep(node.right, deep) // 右子树深度
        return Math.max(leftDeep, rightDeep)
    }
    // 非递归
    getDeep2() {
        let queue = []
        let count = 0
        queue.push(this.root)
        while (queue.length) {
            let len = queue.length // 当前层的节点数
            for (let i = 0; i < len; i++) {
                let node = queue.shift()
                if (node.left) {
                    queue.push(node.left)
                }
                if (node.right) {
                    queue.push(node.right)
                }
            }
            count++
        }
        return count
    }
}

const tree = new BinarySearchTree()
tree.insert(3)
tree.insert(8)
tree.insert(1)
tree.insert(2)
tree.insert(5)
tree.insert(7)
tree.insert(6)
tree.insert(0)

// console.log(tree);
// tree.inOrder(tree.root) //0 1 2 3 5 6 7 8 
// tree.preOrder(tree.root) //3 1 0 2 8 5 7 6 
// tree.postOrder(tree.root) //0 2 1 6 7 5 8 3
// console.log(tree.getMin())
// console.log(tree.getMax())
// console.log(tree.getDeep(tree.root))
// console.log(tree.find(5))
tree.remove(tree.root, 5)
console.log(tree.find(8))