js版数据结构_08 二叉搜索树

知识点:

  • 什么是二叉搜索树
  • 实现

ps:写在前面,树的结构虽然比前面的复杂一点,但是呢只要会用递归其实还是很简单的。
理解递归也是蛮简单的,不信画个执行栈走走。这一节我本来打算是只把代码放上来的,因为除了一个删除比较复杂点别的就是跟着逻辑写,所以文字描述显得比较应付。看代码吧,代码思路写的还是没毛病的

1. 二叉搜索树

二叉树我们都很熟悉,二叉搜索树是在二叉树的基础上又做了一下规定。即只允许你在左侧节点存(比父节点)小的值,在右侧节点存储(比父节点)大的值。
在这里插入图片描述
创建二叉搜素树类:

 class BinarySearchTree {
            constructor() {
                this.root = null;
            }
            }

二叉搜素树的节点要保存以下信息

  • 自身key值
  • 左孩子地址
  • 右孩子地址
    即:二叉搜索树节点结构
        class TreeNode {
            constructor(key) {
                this.key = key;
                this.right = null;
                this.left = null;
            }
        }

我们接下来要实现二叉搜索树的以下方法:

  • add(key)向树中添加节点
  • 遍历(中序先序后序只是输出key的位置不同而已,以中序为例)
  • min()取最小值
  • max()取最大值
  • search(key)判数组是否存在某值
  • remove(key)删除树中某节点

2. 实现

树结构常见操作就是使用递归或者使用循环,这两种方法各有优缺点。递归比较好理解,循环方式效率高,看个人喜好吧。下面的栗子我两种方式都有用到,但是因为递归便于理解故大部分使用的是递归完成的。

add:它的实现比较简单,就是比较key。用于判断程序走向,再用递归向下一步步找下去。找到合适位置放入新节点。

			  add(key) {
                    let treeNode = new TreeNode(key);
                    if (this.root == null) {
                        this.root = treeNode;
                    } else {
                        this.addNode(this.root, treeNode);
                    }
                }
 				addNode(node, newNode) {
                // 右
                if (newNode.key > node.key) {
                    if (node.right == null) {
                        node.right = newNode;
                    } else {
                        this.addNode(node.right, newNode);
                    }
                } else {
                    if (node.left == null) {
                        node.left = newNode;
                    } else {
                        this.addNode(node.left, newNode);
                    }
                }
            }

中序遍历:见代码注释

 			ergodicityS() {
                this.ergodicity(this.root);
            }
            // 中序遍历
            ergodicity(node) {
                    // 基线条件即左到头或右到头,拿程序栈来理解此递归
                    if (node != null) {
                        // 去找左边
                        this.ergodicity(node.left);
                        // 打印
                        console.log(node.key);
                        // 去找右边
                        this.ergodicity(node.right);
                    }
                }

min,max:这里我分别用了一下循环和递归方式。拿最小最大值很简单。因为在二叉搜索树中,最小值是最左边的叶子;最大值是最右边的叶子

  			min() {
                return this.minNode(this.root).key;
            }
            minNode(node) {
                // 最小值在树的最左边
                while (node != null && node.left != null) {
                    node = node.left;
                }
                return node
            }
            max() {
                return this.maxNode(this.root).key;

            }
            maxNode(node) {
                if (node.right == null) {
                    return node;
                } else {
                    return this.maxNode(node.right);
                }
            }

search:还是根据key判断向左还是向右找,递归进去

           // 查询某个特值节点是否存在
            search(key) {
                return this.searchNode(this.root, key);
            }
            searchNode(node, key) {
                if (node === null) {
                    return false;
                }
                if (node.key < key) { //向左找
                    return this.searchNode(node.right, key);

                } else if (node.key > key) { //向右找
                    return this.searchNode(node.left, key);
                } else if (node.key == key) {
                    return true;
                }
            }

remove:这里需要好好说下思路,删除有三种情况,1 叶子 2. 仅有一个孩子 3. 有两个孩子
删除叶子的操作很简单,只要把这个节点设置为null即可;仅有一个孩子也不难,即可以用这个节点的孩子代替要删除节点;有两个孩子的情况较为复杂一点吗,它的继承者是它右子树的左叶子。别的不变,左子树还是原来的左子树,右子树即是那个去掉了左叶子的右子树。

 remove(key) {
                return this.root = this.removeNode(this.root, key);
            }
            removeNode(node, key) {
                // 三种情况需要考虑
                //1 要删除的是叶子
                //2 要删除的节点只有一个孩子
                //3 要删除的节点有两个孩子

                // 先定位到要做删除节点的位置
                if (node.key > key) {
                    // 去左边找
                    node.left = removeNode(node.left, key);
                    return node;

                } else if (node.key < key) {
                    // 去右边找
                    node.right = this.removeNode(node.right, key);
                    return node;

                } else {
                    //进行删除操作
                    // 情况一:
                    if (node.left == null && node.right == null) {
                        node = null;
                        return node;

                    }
                    // 情况二
                    if (node.left == null) {
                        node = node.right;
                        return node;
                    } else if (node.right == null) {
                        node = node.left;
                        return node;
                    }
                    //情况三
                    // 它的继承者是此子树其右子树中最小值,左右子树保持不变(去掉右子树中min之后的)
                    let rightTree = node.right;
                    let currentKey = this.minNode(node.right);
                    node.key = currentKey;
                    // 此时还需要做最后一步,即去除其右子树最小值。即删一个叶子
                    node.right = this.removeNode(node.right, currentKey);
                    return node;

                }

完整代码及部分测试

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        class TreeNode {
            constructor(key) {
                this.key = key;
                this.right = null;
                this.left = null;
            }
        }
        class BinarySearchTree {
            constructor() {
                this.root = null;
            }
            addNode(node, newNode) {
                // 右
                if (newNode.key > node.key) {
                    if (node.right == null) {
                        node.right = newNode;
                    } else {
                        this.addNode(node.right, newNode);
                    }
                } else {
                    if (node.left == null) {
                        node.left = newNode;
                    } else {
                        this.addNode(node.left, newNode);
                    }
                }
            }
            add(key) {
                    let treeNode = new TreeNode(key);
                    if (this.root == null) {
                        this.root = treeNode;
                    } else {
                        this.addNode(this.root, treeNode);
                    }
                }
                // 中序遍历
            ergodicity(node) {
                    // 基线条件即左到头或右到头,拿程序栈来理解此递归
                    if (node != null) {
                        // 去找左边
                        this.ergodicity(node.left);
                        // 打印
                        console.log(node.key);
                        // 去找右边
                        this.ergodicity(node.right);
                    }
                }
                // 中序遍历实调
            ergodicityS() {
                this.ergodicity(this.root);
            }
            min() {
                return this.minNode(this.root).key;
            }
            minNode(node) {
                // 最小值在树的最左边
                while (node != null && node.left != null) {
                    node = node.left;
                }
                return node
            }
            max() {
                return this.maxNode(this.root).key;

            }
            maxNode(node) {
                if (node.right == null) {
                    return node;
                } else {
                    return this.maxNode(node.right);
                }
            }

            // 查询某个特值节点是否存在
            search(key) {
                return this.searchNode(this.root, key);
            }
            searchNode(node, key) {
                if (node === null) {
                    return false;
                }
                if (node.key < key) { //向左找
                    return this.searchNode(node.right, key);

                } else if (node.key > key) { //向右找
                    return this.searchNode(node.left, key);
                } else if (node.key == key) {
                    return true;
                }
            }

            remove(key) {
                return this.root = this.removeNode(this.root, key);
            }
            removeNode(node, key) {
                // 三种情况需要考虑
                //1 要删除的是叶子
                //2 要删除的节点只有一个孩子
                //3 要删除的节点有两个孩子

                // 先定位到要做删除节点的位置
                if (node.key > key) {
                    // 去左边找
                    node.left = removeNode(node.left, key);
                    return node;

                } else if (node.key < key) {
                    // 去右边找
                    node.right = this.removeNode(node.right, key);
                    return node;

                } else {
                    //进行删除操作
                    // 情况一:
                    if (node.left == null && node.right == null) {
                        node = null;
                        return node;

                    }
                    // 情况二
                    if (node.left == null) {
                        node = node.right;
                        return node;
                    } else if (node.right == null) {
                        node = node.left;
                        return node;
                    }
                    //情况三
                    // 它的继承者是此子树其右子树中最小值,左右子树保持不变(去掉左子树中min之后的)
                    let rightTree = node.right;
                    let currentKey = this.minNode(node.right);
                    node.key = currentKey;
                    // 此时还需要做最后一步,即去除其右子树最小值。即删一个叶子
                    node.right = this.removeNode(node.right, currentKey);
                    return node;

                }


            }
        }

        let tree = new BinarySearchTree();
        tree.add(6);
        tree.add(7);
        tree.add(4);
        tree.add(2);
        tree.add(10);
        tree.ergodicityS();
        console.log(tree);
        console.log(tree.min());
        console.log(tree.max());
        tree.remove(7);
        console.log(tree.search(11));
    </script>

</body>

</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值