持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
本篇文章主要是介绍并使用 js 自行封装一个二叉搜索树(Binary Search Tree, BST)。首先,我们来讲讲,什么是树结构。
简单介绍
说到树,可能想到的是浏览器渲染用到的 DOM 树,vue 里用到的虚拟 DOM 树等,它们都是一种非线性数据结构,按照百度百科的话说,就是数据元素(节点)按分支关系组织起来的结构,很像自然界中的树那样。
树结构的表示
普通的树结构,每个节点都可以有很多的子节点,也就是有很多的分支,如下图所示:
如果我们让上图中,每个节点都拥有两个属性,一个属性 left,指向的是该节点的所有子节点中最左边的那个;另一个属性 right,则指向该节点的右边的兄弟节点,那么上图就变成了下图所示:
这样,无论原本的树结构中,一个节点有多少个子节点,都可以转变为一个节点的度(节点的子树个数)不大于 2 的二叉树。而二叉搜索树,就是满足下列特殊条件的二叉树:
- 非空左子树,所有键值都小于其根节点的键值;
- 非空右子树,所有键值都大于其根节点的键值;
- 左右子树本身也是二叉搜索树。
一言以蔽之,就是在有左右子节点的情况下,每个节点的键值必然是大于其左子节点的键值,小于其右子节点的键值。 二叉搜索树的一个特点在于,在查找某个节点时,所需要的最大次数,等于二叉搜索树的深度(规定根节点为第 1 层,其余节点为父节点的层数 + 1,然后树中所有节点里的最大层次为数的深度)。
优点&缺点
二叉搜索树可以快速地根据给定的关键字 key 查找到数据,得到数据所需要的查找次数,最多就等于树的深度。如果二叉搜索树的节点,左右分布是比较均匀的平衡树,那么插入查找的效率是 O(logN)。但是,如果插入的数据是比如 1、2、3、4、5、6、7 这样有序的,就会形成以 1 为根,只有右子树的非平衡树,其实就可以看成是一个单向链表了,其查找效率就会变成 O(N)。这里顺便提一句,如果想让生成的 BST 尽可能是棵平衡树,可以用红黑树这种符合一些规定特性的二叉搜索树结构。
代码封装
整体框架
我们先定义一个 BinarySearchTree 类用于封装一个二叉搜索树结构。它只需要一个 root 属性用于指向根节点,增删改查等方法则在后面慢慢添加。
class BinarySearchTree {
constructor() {
this.root = null
}
}
另外我们还需要以 CreateNode 类用来生成节点。它有 3 个属性:
key:保存节点的键值left:保存节点的左子节点的引用right:保存节点的右子节点的引用
class CreateNode {
constructor(key) {
this.key = key
this.left = null
this.right = null
}
}
方法
增 insert()
insert() 的方法用于向二叉搜索树添加新节点,传入一个 key 作为该节点的键值。思路分析如下:
- 定义
insertNode递归函数,传入两个参数 —— 新节点newNode和节点node(二叉搜索树上的根节点或某个子节点),比较它两的键值大小,如果newNode的键值更小,则应该往node的左子树里放,继续将newNode和node的左子节点作为参数传入insertNode进行递归处理,如果左子节点不存在,则直接将新节点作为左子节点,结束递归。 - 如果
newNode的键值大于等于node的键值,则应该往node的右子树里放,将新节点和右子节点作为参数传入insertNode进行递归。结束递归的条件也是当某个右子节点不存在时,将新节点作为右子节点,插入完成。
// 增
insert(key) {
const newNode = new CreateNode(key)
this.insertNode(newNode, this.root)
}
insertNode(newNode, node) {
// 1.判断原来根节点有没有值
if (this.root) {
// 有值
if (node.key > newNode.key) {
// 如果新节点的 key 更小,则放在左边,判断 node 的 left 是否有值
if (node.left) {
this.insertNode(newNode, node.left)
} else {
node.left = newNode
}
} else {
// 新节点的 key 更大,放右边
if (node.right) {
this.insertNode(newNode, node.right)
} else {
node.right = newNode
}
}
} else {
// 没值
this.root = newNode
}
}
下面插入 7、4、3、5、11 和 8 这几个数:
const bst = new BinarySearchTree()
bst.insert(7)
bst.insert(4)
bst.insert(3)
bst.insert(5)
bst.insert(11)
bst.insert(8)
形成的二叉搜索树应该如下图所示:
至此,本文就先介绍关于二叉搜索树的增加节点的操作,而修改操作,其实可以给节点添加 value 属性,然后存储对应 key 值的数据,只要能通过 key 值找到该节点,自然能对 value 进行相关修改,就不再多费笔墨了。至于查询和删除节点的方法,由于需要考虑的情况较多,将放在之后的篇章继续介绍。