二叉搜索树

什么是二叉搜索树?

  • 二叉搜索树(BST,Binary Search Tree),也称二叉排序树或二叉查找树

  • 二叉搜索树是一颗二叉树, 可以为空;如果不为空,满足以下性质:

    • 非空左子树的所有键值小于其根结点的键值。
    • 非空右子树的所有键值大于其根结点的键值。
    • 左、右子树本身也都是二叉搜索树。
  • 下面哪些是二叉搜索树, 哪些不是

  • 二叉搜索树的特点:

    • 二叉搜索树的特点就是相对较小的值总是保存在左结点上, 相对较大的值总是保存在右结点上.
    • 那么利用这个特点, 我们可以做什么事情呢?
    • 查找效率非常高, 这也是二叉搜索树中, 搜索的来源.

二叉搜索树的操作

  • 二叉搜索树有哪些常见的操作呢?
    • insert(key):向树中插入一个新的键。
    • search(key):在树中查找一个键,如果结点存在,则返回true;如果不存在,则返回false
    • inOrderTraverse:通过中序遍历方式遍历所有结点。
    • preOrderTraverse:通过先序遍历方式遍历所有结点。
    • postOrderTraverse:通过后序遍历方式遍历所有结点。
    • min:返回树中最小的值/键。
    • max:返回树中最大的值/键。
    • remove(key):从树中移除某个键。

二. 二叉搜索树的实现

现在, 我们通过代码来实现二叉搜索树.

创建二叉搜索树

  • 我们像封装其他数据结构一样, 先来封装一个BinarySearchTree的类

    // 创建BinarySearchTree
    function BinarySerachTree() {
        // 创建结点构造函数
        function Node(key) {
            this.key = key
            this.left = null
            this.right = null
        }
        
        // 保存根的属性
        this.root = null
        
        // 二叉搜索树相关的操作方法
    }
    
  • 代码解析:

    • 封装BinarySearchTree的构造函数.
    • 还需要封装一个用于保存每一个结点的类Node.
      • 该类包含三个属性: 结点对应的key, 指向的左子树, 指向的右子树
    • 对于BinarySearchTree来说, 只需要保存根结点即可, 因为其他结点都可以通过根结点找到.

向树中插入数据

  • 我们两个部分来完成这个功能.

  • 外界调用的insert方法

    // 向树中插入数据
    BinarySerachTree.prototype.insert = function (key) {
        // 1.根据key创建对应的node
        var newNode = new Node(key)
        
        // 2.判断根结点是否有值
        if (this.root === null) {
            this.root = newNode
        } else {
            this.insertNode(this.root, newNode)
        }
    }
    
  • 代码解析:

    • 首先, 根据传入的key, 创建对应的Node.
    • 其次, 向树中插入数据需要分成两种情况:
      • 第一次插入, 直接修改根结点即可.
      • 其他次插入, 需要进行相关的比较决定插入的位置.
    • 在代码中的insertNode方法, 我们还没有实现, 也是我们接下来要完成的任务.
  • 插入非根结点

    BinarySerachTree.prototype.insertNode = function (node, newNode) {
        if (newNode.key < node.key) { // 1.准备向左子树插入数据
            if (node.left === null) { // 1.1.node的左子树上没有内容
                node.left = newNode
            } else { // 1.2.node的左子树上已经有了内容
                this.insertNode(node.left, newNode)
            }
        } else { // 2.准备向右子树插入数据
            if (node.right === null) { // 2.1.node的右子树上没有内容
                node.right = newNode
            } else { // 2.2.node的右子树上有内容
                this.insertNode(node.right, newNode)
            }
        }
    }
    
  • 代码解析:

    • 插入其他节点时, 我们需要判断该值到底是插入到左边还是插入到右边.
    • 判断的依据来自于新节点的key和原来节点的key值的比较.
      • 如果新节点的newKey小于原节点的oldKey, 那么就向左边插入.
      • 如果新节点的newKey大于原节点的oldKey, 那么就向右边插入.
    • 代码的1序号位置, 就是准备向左子树插入数据. 但是它本身又分成两种情况
      • 情况一(代码1.1位置): 左子树上原来没有内容, 那么直接插入即可.
      • 情况二(代码1.2位置): 左子树上已经有了内容, 那么就一次向下继续查找新的走向, 所以使用递归调用即可.
    • 代码的2序号位置, 和1序号位置几乎逻辑是相同的, 只是是向右去查找.
      • 情况一(代码2.1位置): 左右树上原来没有内容, 那么直接插入即可.
      • 情况二(代码2.2位置): 右子树上已经有了内容, 那么就一次向下继续查找新的走向, 所以使用递归调用即可.
  • 测试代码: 如果按照下面的代码插入, 最后形成什么样的树呢?

    // 测试代码
    var bst = new BinarySerachTree()
    
    // 插入数据
    bst.insert(11)
    bst.insert(7)
    bst.insert(15)
    bst.insert(5)
    bst.insert(3)
    bst.insert(9)
    bst.insert(8)
    bst.insert(10)
    bst.insert(13)
    bst.insert(12)
    bst.insert(14)
    bst.insert(20)
    bst.insert(18)
    bst.insert(25)
    
  • 形成的树:

  • 如果这个时候, 我新插入一个数据6, 那么插入的位置和顺序应该怎样的呢?

    bst.insert(6)
    
  • 新的树:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值