详解二叉排序树及其基本操作

二叉排序树又称二叉查找树,它是一种对排序和查找都很有用的特殊二叉树。

1. 二叉排序树的定义

二叉排序树或者是一颗空树,或者是具有下列性质的二叉树:
(1) 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2) 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3) 它的左、右子树也分别为二叉排序树。

由定义可得到二叉排序树的一个重要性质:中序遍历一颗二叉排序树时可以得到一个结点值递增的有序序列。

在下面讨论二叉排序树的操作中,使用二叉链表作为存储结构。因为二叉排序树的操作要根据结点的关键字域来进行,所以下面给出了每个结点的数据域的类型定义 ( 包括关键字项和其他数据项 )。

// 二叉排序树的二叉链表存储表示
typedef struct
{KeyType key;                     // 关键字项InfoType otherinfo;              // 其他数据项
}ElemType;                           // 每个结点的数据域的类型
typedef struct BSTNode
{ElemType data;                   // 每个结点的数据域包括关键字项和其他数据项struct BSTNode *lchild, *rchild; // 左右孩子指针
}BSTNode, *BSTree;

2. 二叉排序树的查找

因为二叉排序树可以看成是一个有序表,所以在二叉排序树上进行查找和折半查找类似,也是一个逐步缩小查找范围的过程。

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

【算法步骤】

(1) 若二叉排序树为空,则查找失败,返回空指针。
(2) 若二叉排序树非空,将给定值 key 与根结点的关键字 T->data.key 进行比较:

  • 若 key 等于 T->data.key,则查找成功,返回根结点地址;
  • 若 key 小于 T->data.key,则递归查找左子树;
  • 若 key 大于 T->data.key,则递归查找右子树。

【算法描述】

BSTree SearchBST(BSTree T, KeyType key)
{ // 在根指针 T 所指二叉排序树中递归地查找某关键字等于 key 的数据元素// 若查找成功,则返回指向该数据元素结点的指针,否则返回空指针if ((!T) || key == T->data.key) return T;                     // 查找结束else if (key < T->data.key) return SearchBST(T->lchild, key); // 在左子树中继续查找else return SearchBST(T->rchild, key);                        // 在右子树中继续查找
}

3. 二叉排序树的插入

二叉排序树的插入操作是以查找为基础的。要将一个关键字值为 key 的结点 *S 插入到二叉排序树中,则需要从根结点向下查找,当树中不存在关键字等于 key 的结点时才进行插入。新插入的结点一定是一个新添加的叶子结点,并且是查找不成功时查找路径上访问的最后一个结点的左孩子或右孩子结点。

算法2:二叉排序树的插入

【算法步骤】

(1) 若二叉排序树为空,则待插入结点 *S 作为根结点插入到空树中。
(2) 若二叉排序树非空,则将 key 与根结点的关键字 T->data.key 进行比较:

  • 若 key 小于 T->data.key,则将 *S 插入左子树;
  • 若 key 大于 T->data.key,则将 *S 插入右子树。

【算法描述】

void InsertBST(BSTree &T, ElemType e)
{ // 当二叉排序 T 树中不存在关键字等于 e.key 的数据元素时,则插入该元素if (!T){                                 // 找到插入位置,递归结束S = new BSTNode;              // 生成新结点 *SS->data = e;                  // 新结点 *S 的数据域置为 eS->lchild = S->rchild = NULL; // 新结点 *S 作为叶子结点T = S;                        // 把新结点 *S 链接到已找到的插入位置}else if (e.key < T->data.key)InsertBST(T->lchild, e);      // 将 *S 插入左子树else if (e.key > T->data.key)InsertBST(T->rchild, e);      // 将 *S 插入右子树
}

4. 二叉排序树的创建

二叉排序树的创建是从空的二叉排序树开始的,每输入一个结点,经过查找操作,将新结点插入到当前二叉排序树的合适位置。

算法3:二叉排序树的创建

【算法步骤】

(1) 将二叉排序树 T 初始化为空树。
(2) 读入一个关键字为 key 的结点。
(3) 如果读入的关键字 key 不是输入结束标志,则循环执行以下操作:

  • 将此结点插入二叉排序树 T 中;
  • 读入一个关键字为 key 的结点。

【算法描述】

void CreatBST(BSTree &T)
{ // 依次读入一个关键字为 key 的结点,将此结点插入二叉排序树 T 中T = NULL;                // 将二叉排序树 T 初始化为空树cin >> e;while (e.key != ENDFLAG) // ENDFLAG 为自定义常量,作为输入结束标志{InsertBST(T, e);     // 将此结点插入二叉排序树 T 中cin >> e}
}

5. 二叉排序树的删除

被删除的结点可能是二叉排序树中的任何结点,删除结点后,要根据其位置不同修改其双亲结点及相关结点的指针,以保持二叉排序树的特性。

算法4:二叉排序树的删除

【算法步骤】

首先从二叉排序树的根结点开始查找关键字为 key 的待删结点,如果树中不存在此结点,则不做任何操作;否则,假设被删结点为 *p其双亲结点为 *fplpr 分别表示其左子树和右子树

不失一般性,可设 *p 是 *f 的左孩子 ( 右孩子情况类似 )。下面分三种情况进行讨论。

(1) 若 *p 结点为叶子结点,即 plpr 均为空树。由于删去叶子结点不破坏整棵树的结构,则只需修改其双亲结点的指针即可。

(2) 若 *p 结点只有左子树 pl 或者只有右子树 pr,此时只要令 plpr 直接成为其双亲结点 *f 的左子树即可。

(3) 若 *p 结点的左子树和右子树均不空,则删去 *p 之后令 *p 的直接前驱 *s 替代 *p,然后再从二叉排序树中删去其直接前驱 *s 即可。

【算法描述】

void DeleteBST(BSTree &T, KeyType key)
{ // 从二叉排序树 T 中删除关键字等于 key 的结点p = T; f = NULL; // 初始化/* 查找关键字为 key 的结点 *p */while (p){if(p->data.key == key) break;         // 找到关键字等于 key 的结点 *p 时循环结束f = p;                                // *f 为 *p 的双亲结点if (p->data.key > key) p = p->lchild; // 在 *p 的左子树中继续查找else p = p->rchild;                   // 在 *p 的右子树中继续查找}if (!p) return;                           // 找不到被删结点则返回/* 结点的 3 种删除情况 */q = p;if ((p->lchild) && (p->rchild))           // 被删结点 *p 左右子树均不空{s = p->lchild;while (s->rchild)                     // 在 *p 的左子树中继续查找其前驱结点,即是右下结点{q = s;s = s->rchild;                    // 一直向右}p->data = s->data;                    // s 指向被删结点的前驱if (q != p) q->rchild = s->lchild;    // 重接 *q 的右子树else q->lchild = s->lchild;           // 重接 *q 的左子树delete s;return;  }                                         // ifelse if (!p->rchild) p = p->lchild;       // 被删结点 *p 无右子树,只需重接其左子树else if (!p->lchild) p = p->rchild;       // 被删结点 *p 无左子树,只需重接其右子树/* 将 p 所指的子树挂接到其双亲结点 *f 相应的位置 */if (!f) T = p;                            // 被删结点为根结点else if (q == f->lchild) f->lchild = p;   // 挂接到 *f 的左子树位置else f->rchild = p;                       // 挂接到 *f 的右子树位置delete q;
}

详解二叉排序树及其基本操作相关推荐

  1. 树和二叉树(四种遍历,建树)详解+二叉排序树(包含图像和相关习题)

    目录 树和二叉树 一.树 2.有序树和无序树 3.森林 4.树的基本性质 二.二叉树的概念 (1)二叉树的编号 1.二叉树和度为2的有序树的区别: 2.满二叉树 3.完全二叉树: 4.平衡二叉树: 5 ...

  2. C语言详解双向链表的基本操作

    目录 双链表的定义与接口函数 定义双链表 接口函数 详解接口函数的实现 创建新节点(BuyLTNode) 初始化双链表(ListInit) 双向链表打印(ListPrint) 双链表查找(ListFi ...

  3. 详解双向链表的基本操作(C语言)

    工科生一枚,热衷于底层技术开发,有强烈的好奇心,感兴趣内容包括单片机,嵌入式Linux,Uboot等,欢迎学习交流! 爱好跑步,打篮球,睡觉. 欢迎加我QQ1500836631(备注CSDN),一起学 ...

  4. 【数据结构-树】3.详解二叉排序树(理论+代码)

    二叉排序树 二叉排序树的定义 二叉排序树也称为二叉查找树.二叉排序树或者是一棵空树,或者是一棵具有如下特性的非空为茶树 若左子树非空,则左子树所有结点关键字值均小于根结点的关键字值 若右子树非空,则右 ...

  5. 折半查找和二叉排序树的时间性能_详解二叉排序树(基础篇)

    前言 我们的线性表有2种,无序线性表和有序线性表. 无序线性表正如其名,它里面的数据是无序的,因为这个表不用维护什么东西,所有它的插入和删除操作的效率也不错..但是,正因为他是无序的,查找起来就很废时 ...

  6. 详解二叉排序树(二叉搜索树、二叉查找树)以及Python实现相关操作

    二叉排序树 引言 1.定义 2.性质 3.操作 3.1 查找 3.2 插入 3.3 生成 3.4 删除 引言 如何更加高效的完成对数据的查询和添加操作,例如↓↓↓ 给你一个数列 (7, 3, 10, ...

  7. java ftp详解_Java FTP 基本操作

    最近工作中用到了 FTP 相关的操作,所以借此机会了解了下具体内容. FTP基础 关于 FTP 基础推荐阅读<使用 Socket 通信实现 FTP 客户端程序>,其中需要特别注意的是主动模 ...

  8. TortoiseGit学习系列之TortoiseGit基本操作将提交到本地的项目推送到在线仓库(图文详解)...

    前面博客 TortoiseGit学习系列之TortoiseGit基本操作克隆项目(图文详解) TortoiseGit学习系列之TortoiseGit基本操作修改提交项目(图文详解) TortoiseG ...

  9. Day36.SQL详解

    SQL详解 文章目录 SQL详解 前言 一. 基本操作 1.1 DDL(数据定义语言) 1.2 DML 1.3 DCL 二. 升级操作 2.1 索引 2.2 视图 2.3 存储过程 总结 前言 本文主 ...

最新文章

  1. Servlet 传输中文乱码解决方法
  2. 9款Android经常使用的高速开发框架
  3. 进程管理3--经典的进程同步问题
  4. [原创]安装Ubuntu Server 14.04后
  5. JS小功能(操作Table--动态添加删除表格及数据)实现代码
  6. ITK:计算图像中的局部噪声
  7. jMeter parallel controller 无法使用 CSV Data config 提供的变量?
  8. 递归javascript_JavaScript中的递归
  9. RabbitMQ管理界面简述_入门试炼_第3篇
  10. java indexof_Java Vector indexOf()方法与示例
  11. 洛谷P2134 百日旅行
  12. Tengine新增nginx upstream模块的使用
  13. 简易中控紫猫插件版(1)
  14. SAP ABAP BAPI_MATERIAL_AVAILABILITY 查询可用库存
  15. Kubernetes kubeadm部署
  16. 海康sdk docker虚拟化
  17. IOS屏幕旋转的检测 与 强行切换
  18. IP地址划分与子网掩码
  19. python中randint是什么意思_python中random.randint和random.randrange的区别详解
  20. saltstack的NETAPI接口详讲

热门文章

  1. c语言编写万年历课程设计,用C语言编写万年历 C课程设计.pdf
  2. 51单片机(STC)串口无阻塞发送函数
  3. 如何使自己成为一个幽默的人
  4. 什么是AVIF?如何在您的网站上使用AV1图像格式
  5. 什么是Token(令牌)
  6. 三无电子烟与即将到来的监管时代
  7. echarts自定义主题构建
  8. 浅析 ddl,dml,dql,dcl 概念定义
  9. vue中全局注册和局部注册
  10. 微型投影仪第五篇——Metro UI