什么是二叉查找树(BST)

1. 什么是BST

对于二叉树中的每个节点X,它的左子树中所有项的值都小于X中的项,它的右子树中所有项的值大于X中的项。这样的二叉树是二叉查找树。

以上是一颗二叉查找树,其特点是:

(1)若它的左子树不为空,则左子树上的所有节点的值都小于它的根节点的值;

(2)若它的右子树不为空,则右子树上所有节点的值都大于它的根节点的值;

(3)其他的左右子树也分别为二叉查找树;

(4)二叉查找树是动态查找表,在查找的过程中可见添加和删除相应的元素,在这些操作中需要保持二叉查找树的以上性质。

2. 二叉查找树的定义

根据二叉查找树的要求,我们首先让BinaryNode实现Comparable接口。


// 树节点
class BinaryNode implements Comparable { Integer element; BinaryNode left; BinaryNode right; public BinaryNode{ } public BinaryNode(Integer element, BinaryNode left, BinaryNode right) {
this.element = element;
this.left = left;
this.right = right;
} @Override
public int compareTo(@NonNull Object o) {
return this.element - (Integer) o;
}
}

下面我们完成对BinarySearchTree的定义(只定义关键轮廓,具体接口后续进行分析)。


public class BinarySearchTree { //定义树的根节点
BinaryNode root; public BinarySearchTree() {
this.root = null;
} public void makeEmpty() {
this.root = null;
} public boolean isEmpty() {
return this.root == null;
} // 判断是否包含某个元素
public boolean contains(Integer x) {
//TODO:后续讲解
return false;
} // 查找最小值
public BinaryNode findMin(){
//TODO:后续讲解
return null;
} // 查找最大值
public BinaryNode findMax(){
//TODO:后续讲解
return null;
} // 按照顺序插入值
public void insert(Integer x){
//TODO:后续讲解
} // 删除某个值
public void remove(Integer x){
//TODO:后续讲解
} // 打印树
public void printTree(){
//TODO:后续讲解
}
}

3. contaions操作

如果在树T中包含项X的节点,那么该操作返回true,否则返回false。

树的结构使得这种操作变得非常简单,如果T为空集,直接返回false;否则我们就对T的左子树或右子树进行递归查找,直到找到该项X。

代码实现如下:


/**
* 是否包含某个元素
* @param x 待查找对象
* @return 查找结果
*/
public boolean contains(Integer x) {
// 首次查找从根节点开始
return contains(x,root);
} private boolean contains(Integer x, BinaryNode node) {
// 根节点为空的情况,不需要再查找
if (node == null) {
return false;
} // 与当前节点进行比较
int compareResult = x.compareTo(node.element); // 小于当前节点的值,就递归遍历左子树
if (compareResult < 0) {
return contains(x, node.left);
}
// 大于当前节点的值,就递归遍历右子树
else if (compareResult > 0) {
return contains(x, node.right);
}
// 等于当前节点值,直接返回
else {
return true;
}
}

4. 查找最小节点

从根开始并且只要有左子树就向左进行,终止点就是最小元素节点。


// 查找最小值
public BinaryNode findMin() {
return findMin(root);
} private BinaryNode findMin(BinaryNode node) {
// 当前节点为null,直接返回null
if (node == null) {
return null;
} // 不存在左子树,返回当前节点
if (node.left == null) {
return node;
} // 递归遍历左子树
return findMin(node.left); }

5. 查找最大节点

从根开始并且只要有右子树就向右进行,终止点就是最大元素节点。作为与findMin的对比,findMax方法我们抛弃了常用的递归,使用了常见的while循环来查找。


// 查找最大值
public BinaryNode findMax() {
return findMax(root);
} private BinaryNode findMax(BinaryNode node) {
if (node != null) {
while (node.right != null) {
node = node.right;
}
}
return node;
}

6. insert操作

插入操作在概念上是简单的,为了将X插入树T中,我们像contains一样沿着树查找。

如果找到X,那么我们可以什么都不做,也可以做一些更新操作。

否则,就将X插入到遍历路径上的最后一个节点。(这个做法有待商榷)

代码实现如下:


public void insert(Integer x) {
root = insert(x, root);
} // 返回的插入节点的根节点
private BinaryNode insert(Integer x, BinaryNode node) {
// 如果当前节点为null,新建节点返回
if (node == null) {
return new BinaryNode(x, null, null);
}
// 与当前节点比较
int compareResult = x.compareTo(node.element); // 小于当前节点值,递归插入左子树,并将返回值设置为当前节点的left
if (compareResult < 0) {
node.left = insert(x, node.left);
} // 大于当前节点值,递归插入右子树,并将返回值设置为当前节点的right
if (compareResult > 0) {
node.right = insert(x, node.right);
} // 等于当前的值,不做任何处理
if (compareResult == 0) {
// do some update or do noting
} return node; }

7. 删除操作

正如许多数据结构一样,最难的操作是remove,一旦我们发现了要删除的元素,就要考虑几种可能的情况:

当待删除的节点为叶子节点时,直接删除。

当待删除节点只有一个儿子节点时,把儿子节点代替该节点的位置,然后删除该节点。

当待删除的节点有两个儿子节点时,一般的删除策略是用其右子树的最小的数据代替该节点的数据并递归删除那个节点(现在它是空的);因为右子树的最小节点不可能有左儿子,所以第二次Delete要容易。


// 删除某个值
public void remove(Integer x) {
remove(x, root);
} private BinaryNode remove(Integer x, BinaryNode node) {
if (node == null) {
return null;
} int compareResult = x.compareTo(node.element); if (compareResult < 0) {
node.left = remove(x, node.left);
} if (compareResult > 0) {
node.right = remove(x, node.right);
} if (compareResult == 0) { if (node.left != null && node.right != null) { node.element = findMin(node.right).element; node.right = remove(node.element, node.right); } else { node = (node.left != null) ? node.left : node.right; } } return node; }

8. 二叉查找树的局限

同样的数据,可以对应不同的二叉搜索树,如下:

二叉搜索树可能退化成链表,相应的,二叉搜索树的查找操作是和这棵树的高度相关的,而此时这颗树的高度就是这颗树的节点数n,同时二叉搜索树相应的算法全部退化成 O(n) 级别。

显然,说二叉搜索树的查找、插入、删除这三个操作都是O(lgn) 级别的,只是一个大概的估算,具体要和二叉搜索树的形状相关。但总的来说时间复杂度在o(logn)

到o(n)之间。

9. 简单总结

二叉搜索树的节点查询、构造和删除性能,与树的高度相关,如果二叉搜索树能够更“平衡”一些,避免了树结构向线性结构的倾斜,则能够显著降低时间复杂度。二叉搜索树的存储方面,相对于线性结构只需要保存元素值,树中节点需要额外的空间保存节点之间的父子关系,所以在存储消耗上要高于线性结构。

【数据结构】什么是二叉查找树(BST)的更多相关文章

  1. 查找系列合集-二叉查找树BST

    一. 二叉树 1. 什么是二叉树? 在计算机科学中,二叉树是每个结点最多有两个子树的树结构. 通常子树被称作“左子树”(left subtree)和“右子树”(right subtree). 二叉树常 ...

  2. [学习笔记] 二叉查找树/BST

    平衡树前传之BST 二叉查找树(\(BST\)),是一个类似于堆的数据结构, 并且,它也是平衡树的基础. 因此,让我们来了解一下二叉查找树吧. (其实本篇是作为放在平衡树前的前置知识的,但为了避免重复 ...

  3. 二叉查找树(BST)

    二叉查找树(BST):使用中序遍历可以得到一个有序的序列

  4. 二叉查找树BST 模板

    二叉查找树BST 就是二叉搜索树 二叉排序树. 就是满足 左儿子<父节点<右儿子 的一颗树,插入和查询复杂度最好情况都是logN的,写起来很简单.   根据BST的性质可以很好的解决这些东 ...

  5. 【查找结构 2】二叉查找树 [BST]

    当所有的静态查找结构添加和删除一个数据的时候,整个结构都需要重建.这对于常常需要在查找过程中动态改变数据而言,是灾难性的.因此人们就必须去寻找高效的动态查找结构,我们在这讨论一个非常常用的动态查找树— ...

  6. 数据结构☞二叉搜索树BST

    二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它可以是一棵空树,也可以是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 若它 ...

  7. 数据结构-自平衡二叉查找树(AVL)详解

    介绍: 在计算机科学中,AVL树是最先发明的自平衡二叉查找树. 在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树. 查找.插入和删除在平均和最坏情况下都是O(log n).增 ...

  8. 二叉查找树(BST)的实现

    一.二叉树介绍 二叉查找树(Binary Search Tree,BST),又称二叉排序树,也称二叉搜索树,它或者是一颗空树,或者具有如下性质的树:若它的左子树不为空,则左子树上所有节点的值都小于根节 ...

  9. K:二叉查找树(BST)

    相关介绍:  二叉查找树(英语:Binary Search Tree),也称二叉搜索树.有序二叉树(英语:ordered binary tree),排序二叉树(英语:sorted binary tre ...

  10. 3.2 符号表之二叉查找树BST

    一.插入和查找 1.二叉查找树(Binary Search Tree)是一棵二叉树,并且每个结点都含有一个Comparable的键,保证每个结点的键都大于其左子树中任意结点的键而小于其右子树的任意结点 ...

随机推荐

  1. BeetleX之TCP服务应用详解

    BeetleX是.net core平台下的一个开源TCP 通讯组件,它不仅使用简便还提供了出色性能的支持,可以轻易让你实现上百万级别RPS吞吐的服务应用.组件所提供的基础功能也非常完善,可以让你轻易扩 ...

  2. 在MSSQL中的简单数据类型递归

    在某些特定的项目需求中,我们需要实现树状数据结构, 由此,我们需要用递归将数据查询出来. WITH T AS ( SELECT ID,PID FROM TableName WHERE ID=1 UNI ...

  3. 常见面试题之*args

    这个地方理解即可,只是面试的时候会被问到,单独做了一下知识点的整理,不推荐使用. def self_max(a,b,c,d,e,f,g,h,k,x=1,y=3,z=4): #默认参数 print(a, ...

  4. 初学者接金币游戏C#+unity

  5. c++之指针

    一.指针的基本概念 指针的作用:可以通过指针间接访问内存. 内存编号是从0开始记录的,一般用十六进制数字表示. 可以利用指针变量保存地址. 二.指针变量的定义和使用 指针变量定义语法:数据类型 *变量 ...

  6. 全平台轻量开源verilog仿真工具iverilog+GTKWave使用教程

    前言 如果你只是想检查Verilog文件的语法是否有错误,然后进行一些基本的时序仿真,那么Icarus Verilog 就是一个不错的选择.相比于各大FPGA厂商的IDE几个G的大小,Icarus V ...

  7. SpringBoot之【mybatisplus】代码生成器

    1.概述. AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity.Mapper.Mapper XML.Service.C ...

  8. c++-面向对象:类和对象

    类和对象 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string.h> using names ...

  9. Linux 命令行初始化MySQL数据库

    作为一个技术纯小白,在Linux服务器初始化MySQL数据库的时候遇到了一点小问题: ​ 1.不会使用MySQL图形工具,几乎没玩过 ​ 2.客户的VPN没有开放3306端口,没法用navicat等工 ...

  10. [20191206]隐含参数_db_always_check_system_ts.txt

    [20191206]隐含参数_db_always_check_system_ts.txt --//今年年头我做tab$删除恢复时,遇到的问题,就是遇到延迟块清除的问题.参考链接:http://blog ...