摘要

构造一颗二叉排序树(也叫二叉搜索树,BST,Binary Search Tree)十分简单。一般来讲,大于根节点的放在根节点的右子树上,小于根节点的放在根节点的左子树上(如果等于根节点,则可视情况而定),如果写程序的话,可以采用递归的方式,而且由于不存在重叠子问题的情况,因此递归的性能已经足够好(不考虑栈溢出的情况)。

二叉排序树在通常情况下可以达到O(lgN)的静态、动态操作的时间复杂度,但是存在一种特殊情况,即输入的数据本身就是有序的,这时二叉排序树退化成向量。

下面我们系统归纳一下二叉树的特性,以及相关操作及其代码实现。

二叉排序树

简称BST,也称为二叉查找树。其或是一棵空树,或是一棵具有下列特性的非空二叉树:

1)若左子树非空,则左子树上所有结点关键字值均小于根结点的关键字值。

2)若右子树非空,则右子树上所有结点关键字值均大于根结点的关键字值。

3)左、右子树本身也分别是一棵二叉排序树。

其是一个递归的数据结构。

左子树结点值 < 根结点值 < 右子树结点值

对其进行中序遍历可以得到一个递增的有序序列。

图1. 二叉排序树示例图

CRUD操作

Create-构造二叉排序树

构造一棵二叉排序树就是依次输入数据元素,并将它们插入到二叉树中的适当位置上的过程。

具体过程:

1)每读入一个元素,就建立一个新节点。

2)若二叉排序树非空,则将新结点的值与根结点的值比较。如果小于根结点的值,则插入到左子树中,否则插入到右子树中。

3)若二叉排序树为空,则新结点作为二叉树的根结点。

void Create_BST(BiTree &T, KeyType str[], int n) {
//用关键字数组str[]建立一个二叉排序树
T = NULL; //初始时bt为空树
int i = 0;
while(i < n) { //依次将每个元素插入
BST_Insert(T, str[i]);
i++;
}
}

Retrieve-查找二叉排序树的某结点

二叉排序树的查找是从根结点开始,沿某一分支逐层向下进行比较的一个递归的过程。

具体查找过程是:

1)若二叉树非空,将给定值与根结点的关键字比较,若相等,则查找成功;

2)若不等,则当根结点的关键字大于给定关键字值k时,在根结点的左子树中查找;

3)当根结点的关键字小于给定关键字值k时,在根结点的右子树中查找。

二叉排序树的非递归查找算法:

BSTNode *BST_Search(BiTree T,ElemTypr key,BSTNode *&p) {
//查找函数返回值指向关键字值为key的结点指针,若不存在,返回NULL
p = NULL; //p指向被查找结点的双亲,用于插入和删除操作中
while(T != NULL && key != T->data) {
p = T;
if(key < T->data) {
T = T->lchild;
} else {
T = T->rchild;
}
return T;
}
}

Update-插入结点到二叉排序树中

二叉排序树作为一种动态集合,其特点是树的结构通常不是一次生成的,而是在查找过程中,当树中不存在关键字等于给定值的结点时再进行插入。

由于二叉排序树是递归定义的,其插入结点的过程是:

1)若原二叉树为空,则直接插入结点;

2)否则,若关键字k小于根结点关键字,则插入到左子树中;

3)若关键字k大于根结点关键字,则插入到右子树中。

int BST_Insert(BiTree &T, KeyType k) {
//在二叉=排序树T中插入一个关键字为k的结点
if(T == NULL) {
T = (BiTree)malloc(sizeof(BSTNode));
T->key = k;
T->lchild = T->rchild = NULL;
return 1; //返回1,表示成功
} else if(k == T-> key) { //树中存在相同关键字的结点
return 0;
} else if(k < T->key) { //插入到T的左子树中
return BST_Insert(T->lchild, k);
} else {
return BST_Insert(T->rchild, k);
}
}

由此可见,插入的新结点一定是某个叶结点。下图是向二叉树插入结点28的过程,其中虚线表示查找路径。

图2. 向二叉排序树插入结点28

Delete-删除二叉树的结点

在二叉排序树中删除一个结点时,不能把以该结点为根的子树上的结点都删除,必须把被删除结点从存储二叉排序树的链表上摘下,将因删除结点而断开的二叉链表重新链接起来,同时确保二叉排序树的性质不会丢失。

删除操作的实现过程按3种情况来处理:

1)如果被删除结点z是叶结点,则直接删除,不会破坏二叉排序树的性质。

2)若结点z只有一棵左子树或右子树,则让z的子树成为z父结点的子树,替代z的位置。

3)若结点z有左、右两棵子树,则令z的直接后继【中序第一个子女】(或直接前驱)替代z,然后从二叉排序树中删去这个直接后继【中序第一个子女】(或直接前驱),这样就转换成了第一或第二种情况。

图3. 3种情况下的删除过程

参考

[1] miao_zheng. 二叉排序树、平衡二叉树、B树&B+树、红黑树的设计动机、缺陷与应用场景[OL]. cnblogs, 2018-02-28/2020-06-20

[2] 王道论坛. 2019年数据结构考研复习指导[M].北京:电子工业出版社, 2018:153-155.

二叉排序树BST及CRUD操作的更多相关文章

  1. 二叉排序树(BST)创建,删除,查找操作

    binary search tree,中文翻译为二叉搜索树.二叉查找树或者二叉排序树.简称为BST 一:二叉搜索树的定义 他的定义与树的定义是类似的,也是一个递归的定义: 1.要么是一棵空树 2.如果 ...

  2. 二叉排序树(BST)构造与应用

             二叉排序树(BST)构造与应用       本文取自<数据结构与算法>(C语言版)(第三版).出版社是清华大学出版社.       本博文作为学习资料整理. 源码是VC+ ...

  3. 【翻译】MongoDB指南/CRUD操作(四)

    [原文地址]https://docs.mongodb.com/manual/ CRUD操作(四) 1 查询方案(Query Plans) MongoDB 查询优化程序处理查询并且针对给定可利用的索引选 ...

  4. 【翻译】MongoDB指南/CRUD操作(三)

    [原文地址]https://docs.mongodb.com/manual/ CRUD操作(三) 主要内容: 原子性和事务(Atomicity and Transactions),读隔离.一致性和新近 ...

  5. 【翻译】MongoDB指南/CRUD操作(二)

    [原文地址]https://docs.mongodb.com/manual/ MongoDB CRUD操作(二) 主要内容: 更新文档,删除文档,批量写操作,SQL与MongoDB映射图,读隔离(读关 ...

  6. 【翻译】MongoDB指南/CRUD操作(一)

    [原文地址]https://docs.mongodb.com/manual/ MongoDB CRUD操作(一) 主要内容:CRUD操作简介,插入文档,查询文档. CRUD操作包括创建.读取.更新和删 ...

  7. ASP.NET Core Web API Cassandra CRUD 操作

    在本文中,我们将创建一个简单的 Web API 来实现对一个 “todo” 列表的 CRUD 操作,使用 Apache Cassandra 来存储数据,在这里不会创建 UI ,Web API 的测试将 ...

  8. MongoDB的CRUD操作

    1. 前言 在上一篇文章中,我们介绍了MongoDB.现在,我们来看下如何在MongoDB中进行常规的CRUD操作.毕竟,作为一个存储系统,它的基本功能就是对数据进行增删改查操作. MongoDB中的 ...

  9. 【Java EE 学习 44】【Hibernate学习第一天】【Hibernate对单表的CRUD操作】

    一.Hibernate简介 1.hibernate是对jdbc的二次开发 2.jdbc没有缓存机制,但是hibernate有. 3.hibernate的有点和缺点 (1)优点:有缓存,而且是二级缓存: ...

  10. 使用MyBatis对表执行CRUD操作

    一.使用MyBatis对表执行CRUD操作——基于XML的实现 1.定义sql映射xml文件 userMapper.xml文件的内容如下: <?xml version="1.0&quo ...

随机推荐

  1. CTF-CRYPTO-ECC(1)

    CTF-CRYPTO-ECC(1) 椭圆加密 1.简介 椭圆曲线密码学(Elliptic curve cryptography),简称 ECC,和RSA.ElGamel 算法等类似,是一种公开秘钥加密 ...

  2. Can't find PHP headers in /usr/include/php The php-devel package is required for use of this command.

    报错 phpize 编译扩展时,报错:Can't find PHP headers in /usr/include/php The php-devel package is required for ...

  3. go strings包

    //是否包含指定的字符串中任意一个字符 有一个出现过 就返回true fmt.Println(strings.ContainsAny(s1,"glass")) //返回指定字符出现 ...

  4. Go配置管理神器—Viper中文教程

    Viper中文教程 Viper是适用于Go应用程序的完整配置解决方案.它被设计用于在应用程序中工作,并且可以处理所有类型的配置需求和格式. 安装 go get github.com/spf13/vip ...

  5. Delphi Richedit代码语法加亮显示

    procedure CodeColors(Form : TForm;Style : String; RichE : TRichedit;InVisible : Boolean); const // s ...

  6. Redis 原理 - Hash

    Hash 数据结构 使用 ziplist 当同时满足下面两个条件时,使用 ziplist 存储数据 元素个数少于512个 (hash-max-ziplist-entries: 512) 每个元素长度小 ...

  7. MySQL-脏页的刷新机制

    MySQL内存结构-缓冲区 MySQL的缓冲区中有数据页,索引页,插入缓冲等等,这个角度是从页的功能来分类的.本小节从另一个视角关注这些页,如果从 是否被修改过(和磁盘不一致) 这个角度来区分这些页, ...

  8. Debian 下安装 Nginx

    Debian 下安装 Nginx 非常容易. apt update apt install nginx 输入以下命令查看是否可以正常访问, 顺便验证下安装是否成功. curl -I 127.0.0.1 ...

  9. Asp.net mvc基础(九)使用DropDownList下拉列表

    第一种下拉列表写法: 后端 前端 第二种下拉列表写法: 使用Html辅助方法@Html.DropDownList("名称","List<SelectListItem ...

  10. MySQL 中 INNER JOIN、LEFT JOIN 和 RIGHT JOIN 的区别是什么?

    在MySQL中,INNER JOIN.LEFT JOIN和RIGHT JOIN是用于连接两个或多个表的操作符,它们的主要区别在于如何处理匹配的记录和不匹配的记录. INNER JOIN: 只返回两个表 ...