二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它可以是一棵空树,也可以是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。二叉搜索树作为一种经典的数据结构,它既有链表的快速插入与删除操作的特点,又有数组快速查找的优势;所以应用十分广泛,例如在文件系统和数据库系统一般会采用这种数据结构进行高效率的排序与检索操作

树的定义

树由一组以边连接的节点组成。公司的组织结构图就是一个树的例子,参见下图

组织结构图是用来描述一个组织的架构。在图 10-1 中,每个方框都是一个节点,连接方框

的线叫做边。节点代表了该组织中的各个职位,边描述了各职位间的关系。比如,CIO 直

接汇报给 CEO,那么两者就用一条边连接起来。开发经理向 CIO 汇报,也用一条边连接

起来。销售副总监和开发经理没有直接的联系,因此两个节点间没有用一条边相连。

下图的树展示了更多有关树的术语,在后续讨论中将会提到。一棵树最上面的节点称为

根节点,如果一个节点下面连接多个节点,那么该节点称为父节点,它下面的节点称为子

节点。一个节点可以有 0 个、1 个或多个子节点。没有任何子节点的节点称为叶子节点。

沿着一组特定的边,可以从一个节点走到另外一个与它不直接相连的节点。从一个节点到另一个节点的这一组边称为路径,在图中用虚线表示。以某种特定顺序访问树中所有的节点称为树的遍历。

树可以分为几个层次,根节点是第 0 层,它的子节点是第 1 层,子节点的子节点是第2层,以此类推。树中任何一层的节点可以都看做是子树的根,该子树包含根节点的子节点,子节点的子节点等。我们定义树的层数就是树的深度。

这种自上而下的树与人们的直觉相反。现实世界里,树的根是在底下的。在计算机科学里,自上而下的树则是个由来已久的习惯。事实上,计算机科学家高德纳曾经试图改变这个习惯,但没几个月他就发现,大多数计算机科学家都不愿用自然的、自下而上的方式描述树,于是,这件事也就只好不了了之。最后,每个节点都有一个与之相关的值,该值有时被称为键。

二叉树和二叉查找树

叉排序树的查找过程和次优二叉树类似,通常采取二叉链表作为二叉排序树的存储结构。中序遍历二叉排序树可得到一个关键字的有序序列,一个无序序列可以通过构造一棵二叉排序树变成一个有序序列,构造树的过程即为对无序序列进行排序的过程。每次插入的新的结点都是二叉排序树上新的叶子结点,在进行插入操作时,不必移动其它结点,只需改动某个结点的指针,由空变为非空即可。搜索,插入,删除的复杂度等于树高,O(log(n))

二叉树每个节点的子节点不允许超过两个。通过将子节点的个数限定为 2,可以写出高效的程序在树中插入、查找和删除数据。

下图展示了一棵二叉树

当考虑某种特殊的二叉树,比如二叉查找树时,确定子节点非常重要。二叉查找树是一种特殊的二叉树,相对较小的值保存在左节点中,较大的值保存在右节点中。这一特性使得查找的效率很高,对于数值型和非数值型的数据,比如单词和字符串,都是如此。

实现二叉查找树

实现Node

二叉查找树由节点组成,所以我们要定义的第一个对象就是 Node,该对象和链表类似。Node 类的定义如下:

class Node {
constructor(key) {
this.key = key;
this.left = null;
this.right = null;
}
}

Node 对象既保存数据,也保存和其他节点的链接(left 和 right)。

实现BST

现在可以创建一个类,用来表示二叉查找树(BST)。我们让类只包含一个数据成员:一个表示二叉查找树根节点的 Node 对象。该类的构造函数将根节点初始化为 null,以此创建一个空节点。BST 先要有一个 insert() 方法,用来向树中加入新节点。这个方法有点复杂,需要着重讲解。首先要创建一个 Node 对象,将数据传入该对象保存。

其次检查 BST 是否有根节点,如果没有,那么这是棵新树,该节点就是根节点,这个方法到此也就完成了;否则,进入下一步。

如果待插入节点不是根节点,那么就需要准备遍历 BST,找到插入的适当位置。该过程类似于遍历链表。用一个变量存储当前节点,一层层地遍历 BST。

进入 BST 以后,下一步就要决定将节点放在哪个地方。找到正确的插入点时,会跳出循环。查找正确插入点的算法如下。

  1. 设根节点为当前节点。
  2. 如果待插入节点保存的数据小于当前节点,则设新的当前节点为原节点的左节点;反
  3. 之,执行第 4 步。
  4. 如果当前节点的左节点为 null,就将新的节点插入这个位置,退出循环;反之,继续
  5. 执行下一次循环。
  6. 设新的当前节点为原节点的右节点。
  7. 如果当前节点的右节点为 null,就将新的节点插入这个位置,退出循环;反之,继续
  8. 执行下一次循环。

    有了上面的算法,就可以开始实现 BST 类了。
class BinarySearchTree {
constructor() {
this.root = null;
}
insert(key) {
// 插入
const newNode = new Node(key);
if (this.root === null) {
this.root = newNode;
} else {
this.insertNode(this.root, newNode);
}
}
insertNode(node, newNode) {
if (newNode.key < node.key) {
if (node.left === null) {
node.left = newNode;
} else {
this.insertNode(node.left, newNode);
}
} else {
if (node.right === null) {
node.right = newNode;
} else {
this.insertNode(node.right, newNode);
}
}
}
}

有三种遍历 BST 的方式:中序、先序和后序。中序遍历按照节点上的键值,以升序访问BST 上的所有节点。先序遍历先访问根节点,然后以同样方式访问左子树和右子树。后序遍历先访问叶子节点,从左子树到右子树,再到根节点





增加中序、先序和后序遍历

class BinarySearchTree {
...
inOrderTraverse(callback) {
// 中序查找
this.inOrderTraverseNode(this.root, callback);
}
preOrderTraverse(callback) {
// 先序查找
this.preOrderTraverseNode(this.root, callback);
}
postOrderTraverse(callback) {
// 后序查找
this.postOrderTraverseNode(this.root, callback);
}
inOrderTraverseNode(node, callback) {
if (node !== null) {
this.inOrderTraverseNode(node.left, callback);
callback(node.key);
this.inOrderTraverseNode(node.right, callback);
}
}
preOrderTraverseNode(node, callback) {
if (node !== null) {
callback(node.key);
this.preOrderTraverseNode(node.left, callback);
this.preOrderTraverseNode(node.right, callback);
}
}
postOrderTraverseNode(node, callback) {
if (node !== null) {
this.postOrderTraverseNode(node.left, callback);
this.postOrderTraverseNode(node.right, callback);
callback(node.key);
}
}
}

在二叉查找树上进行查找

对 BST 通常有下列三种类型的查找:

  1. 查找给定值;
  2. 查找最小值;
  3. 查找最大值。
class BinarySearchTree {
...
min() {
// 最小值
return this.minNode(this.root);
}
max() {
// 最大值
return this.maxNode(this.root);
}
search(key) {
// 查找
this.searchNode(this.root, key);
}
minNode(node) {
if (node) {
while (node && node.left !== null) {
node = node.left;
}
return node.key;
}
return null;
}
maxNode(node) {
if (node) {
while (node && node.right !== null) {
node = node.right;
}
return node.key;
}
return null;
}
searchNode(node, key) {
if (node === null) return false;
if (key < node.key) {
return this.searchNode(node.left, key);
} else if (key > node.key) {
return this.searchNode(node.right, key);
} else {
return true;
}
}
findMinNode(node) {
if (node) {
while (node && node.left !== null) {
node = node.left;
}
return node.key;
}
return null;
}
}

从二叉查找树上删除节点

class BinarySearchTree {
...
remove(key) {
//移除树节点
this.removeNode(this.root, key);
}
removeNode(node, key) {
if (node === null) return null; if (key < node.key) {
node.left = this.removeNode(node.left, key);
return node;
} else if (key > node.key) {
node.right = this.removeNode(node.right, key);
return node;
} else {
if (node.left === null && node.right === null) {
node = null;
return node;
} else if (node.left === null) {
node = node.right;
return node;
} else if (node.right === null) {
node = node.left;
return node;
} const aux = this.findMinNode(node.right);
node.key = aux.key;
node.right = this.removeNode(node.right, aux.key);
return node;
}
}
}

完整代码

class Node {
constructor(key) {
this.key = key;
this.left = null;
this.right = null;
}
} class BinarySearchTree {
constructor() {
this.root = null;
}
insert(key) {
// 插入
const newNode = new Node(key);
if (this.root === null) {
this.root = newNode;
} else {
this.insertNode(this.root, newNode);
}
console.log(this.root);
}
inOrderTraverse(callback) {
// 中序查找
this.inOrderTraverseNode(this.root, callback);
}
preOrderTraverse(callback) {
// 先序查找
this.preOrderTraverseNode(this.root, callback);
}
postOrderTraverse(callback) {
// 后序查找
this.postOrderTraverseNode(this.root, callback);
}
min() {
// 最小值
return this.minNode(this.root);
}
max() {
// 最大值
return this.maxNode(this.root);
}
search(key) {
// 查找
this.searchNode(this.root, key);
}
remove(key) {
//移除树节点
this.removeNode(this.root, key);
}
insertNode(node, newNode) {
if (newNode.key < node.key) {
if (node.left === null) {
node.left = newNode;
} else {
this.insertNode(node.left, newNode);
}
} else {
if (node.right === null) {
node.right = newNode;
} else {
this.insertNode(node.right, newNode);
}
}
}
inOrderTraverseNode(node, callback) {
if (node !== null) {
this.inOrderTraverseNode(node.left, callback);
callback(node.key);
this.inOrderTraverseNode(node.right, callback);
}
}
preOrderTraverseNode(node, callback) {
if (node !== null) {
callback(node.key);
this.preOrderTraverseNode(node.left, callback);
this.preOrderTraverseNode(node.right, callback);
}
}
postOrderTraverseNode(node, callback) {
if (node !== null) {
this.postOrderTraverseNode(node.left, callback);
this.postOrderTraverseNode(node.right, callback);
callback(node.key);
}
}
minNode(node) {
if (node) {
while (node && node.left !== null) {
node = node.left;
}
return node.key;
}
return null;
}
maxNode(node) {
if (node) {
while (node && node.right !== null) {
node = node.right;
}
return node.key;
}
return null;
}
searchNode(node, key) {
if (node === null) return false;
if (key < node.key) {
return this.searchNode(node.left, key);
} else if (key > node.key) {
return this.searchNode(node.right, key);
} else {
return true;
}
}
removeNode(node, key) {
if (node === null) return null; if (key < node.key) {
node.left = this.removeNode(node.left, key);
return node;
} else if (key > node.key) {
node.right = this.removeNode(node.right, key);
return node;
} else {
if (node.left === null && node.right === null) {
node = null;
return node;
} else if (node.left === null) {
node = node.right;
return node;
} else if (node.right === null) {
node = node.left;
return node;
} const aux = this.findMinNode(node.right);
node.key = aux.key;
node.right = this.removeNode(node.right, aux.key);
return node;
}
}
findMinNode(node) {
if (node) {
while (node && node.left !== null) {
node = node.left;
}
return node.key;
}
return null;
}
}

参考书籍

数据结构与算法JavaScript描述

数据结构☞二叉搜索树BST的更多相关文章

  1. 数据结构-二叉搜索树(BST binary search tree)

    本文由@呆代待殆原创,转载请注明出处:http://www.cnblogs.com/coffeeSS/ 二叉搜索树简介 顾名思义,二叉搜索树是以一棵二叉树来组织的,这样的一棵树可以用一个链表数据结构来 ...

  2. 数据结构---二叉搜索树BST实现

    1. 二叉查找树 二叉查找树(Binary Search Tree),也称为二叉搜索树.有序二叉树(ordered binary tree)或排序二叉树(sorted binary tree),是指一 ...

  3. C++版 - 剑指offer 面试题24:二叉搜索树BST的后序遍历序列(的判断) 题解

    剑指offer 面试题24:二叉搜索树的后序遍历序列(的判断) 题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则返回true.否则返回false.假设输入的数组的任意两个 ...

  4. 萌新笔记之二叉搜索树(BST)

    前言,以前搞过线段树,二叉树觉得也就那样= =.然后数据结构的课也没怎么听过,然后下周期中考... 本来以为今天英语考完可以好好搞ACM了,然后这个数据结构期中考感觉会丢人,还是好好学习一波. 二叉搜 ...

  5. 给定一个二叉搜索树(BST),找到树中第 K 小的节点

    问题:给定一个二叉搜索树(BST),找到树中第 K 小的节点. 出题人:阿里巴巴出题专家:文景/阿里云 CDN 资深技术专家. 考察点: 1. 基础数据结构的理解和编码能力 2.  递归使用 参考答案 ...

  6. hdu 3791:二叉搜索树(数据结构,二叉搜索树 BST)

    二叉搜索树 Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) Total Submiss ...

  7. 看动画学算法之:二叉搜索树BST

    目录 简介 BST的基本性质 BST的构建 BST的搜索 BST的插入 BST的删除 简介 树是类似于链表的数据结构,和链表的线性结构不同的是,树是具有层次结构的非线性的数据结构. 树是由很多个节点组 ...

  8. 在二叉搜索树(BST)中查找第K个大的结点之非递归实现

    一个被广泛使用的面试题: 给定一个二叉搜索树,请找出其中的第K个大的结点. PS:我第一次在面试的时候被问到这个问题而且让我直接在白纸上写的时候,直接蒙圈了,因为没有刷题准备,所以就会有伤害.(面完的 ...

  9. 二叉搜索树 (BST) 的创建以及遍历

    二叉搜索树(Binary Search Tree) : 属于二叉树,其中每个节点都含有一个可以比较的键(如需要可以在键上关联值), 且每个节点的键都大于其左子树中的任意节点而小于右子树的任意节点的键. ...

随机推荐

  1. 运用Spock编写高质量单元测试

    单元测试作为提升代码质量的有效方法,目前在国内各大互联网公司的开发团队中,尤其是业务团队中却鲜少被使用.这主要由于大家对于单元测试有一些认知错误,或者没有正确的打开方式.至今我们团队在小剧场.零代码运 ...

  2. 配置伪分布模式下的hadoop以及采用fuse-dfs来访问HDFS

    实验目标 配置环境的主要目的是得到HDFS的客户端fuse-dfs的IO性能.本来的服务器上没有任何环境,因此安装均是从无到有的.系统是Ubuntu server 14.04 amd64.整个过程参考 ...

  3. MacOS下PHP7.1升级到PHP7.4.15

    最近写SDK的时候需要用到object类型提示符,PHPStorm智能提示说需要PHP7.2以上才能支持这种类型提示. 我一查我本机的PHP是7.1.30版本,于是考虑升级一下PHP版本. 首先要尝试 ...

  4. 【Notes_1】现代图形学入门——计算机图形学概述

    跟着闫令琪老师的课程学习,总结自己学习到的知识点 课程网址GAMES101 B站课程地址GAMES101 课程资料百度网盘[提取码:0000] 计算机图形学概述 计算机图形学是一门将模型转化到屏幕上图 ...

  5. HashMap是如何进行扩容的?

    HashMap通过resize()方法进行扩容. 源码解析: resize()函数有两种使用情况: 一.当table数组为null时初始化hash表. 二.当table数组不为null时进行扩容. 1 ...

  6. base64图片显示问题

    1.问题描述 小程序项目需要后端接口提供base64流的图片,对于H5的语法,前面拼接后面的代码即可: data:image/png;base64, 先看后台代码: @RestController @ ...

  7. CMD(命令提示符)的基本操作(文件)

    打开CMD窗口,接下来将介绍如何使用CMD来创建.删除.修改.查看文件 1.1 使用CMD创建空文件(为了更好的演示,本文皆以D盘为当前路径),命令如下: copy nul xxx.xx(文件名) 命 ...

  8. css3自动换行排列

    如果一行放不下就会自动换行 display: flex; flex-wrap: wrap; 示例 : html <div class="container"> < ...

  9. nacos集群

    本章分析一下nacos集群之间nacos服务器上线,下线原理 每5秒运行定时任务ServerListManager.ServerListUpdater获取新上线的节点或下线的节点 每2秒运行定时任务S ...

  10. Python函数注解

    目录 函数注解概述 实际应用 inspect模块 业务代码 总结 以下内容基于Python 3x 涉及的知识前提: 建议理解Python装饰器后学习此内容 函数注解概述 函数注解可以针对函数的参数.返 ...