搜索树数据结构支持许多动态几何操作,包括SEARCH、MININUM、MAXINUM、PREDECESSOR、SUCCESSOR、INSERT和DELETE等。因此,我们可以使用一个搜索树作为字典或者优先队列。

二叉搜索树上的基本操作所花费的时间与所花费的时间与这棵树的高度成正比。对于有n个结点的一棵完全二叉树来说,这些操作的最坏运行时间为Θ(logn)。然而如果这棵树是一条n个结点构成的线性链表(最坏情况下),那么同样的操作就要花费Θ(n)的最坏运行时间。

二叉搜索树是以二叉树来组织的结构,它可以使用链表数据结构来表示,每个节点(BSTreeNode)就是一个对象,用于排序的数据属性称为key,每个节点除了key和卫星属性外,还包含left、right和p,分别指向节点的左孩子、右孩子和双亲。如果某个孩子结点和父结点不存在,相应的属性则为NIL。

class BSTreeNode{int key;Object value;//dataBSTreeNode left;BSTreeNode right;BSTreeNode p;//parentpublic BSTreeNode(int key){this.key = key;}
}

二叉搜索树总是满足以下性质:设x是二叉搜索树中的一个结点。如果y是x的左子树中的一个结点,那么y.key <= x.key。如果y是x右子树中的一个结点,那么y.key >= x.key。

二叉搜索树性质允许我们通过中序遍历(inorder tree walk)算法按序输出树中的所有关键字。对于一棵有n个结点的子树调用InorderTreeWalk算法,需要Θ(n)的时间。

public static void InorderTreeWalk(BSTreeNode x){if(x != null){InorderTreeWalk(x.left);System.out.println(x.key);InorderTreeWalk(x.right);}
}

查询二叉树

查询二叉树——查找关键字k

为了查找二叉搜索树中给定的关键字的结点,需要输入树根的指针和关键字k,如果这个结点存在,TreeSearch返回指向这个关键字为k的指针;否则返回NULL。

TreeSearch从树根开始查找,并沿着这棵树中的一条简单路径向下进行,对于遇到的么个结点x,比较关键字k与x.key,如果两个关键字相等,查找就终止,如果k < x.key,则继续在x的左子树上查找,否则就在x的右子树上查找。总的来说肯定会在树根到叶节点间的一条简单路径上遇到或者没遇到目标结点,所以TreeSearch的运行时间为O(h),其中h代表这棵树的高度。

public static BSTreeNode TreeSearch(BSTreeNode x, int k){if(x == null || k == x.key){return x;}if(k < x.key)return TreeSearch(x.left, k);else return TreeSearch(x.right, k);
}

例如要搜索结点5,从树根6开始,关键字5比6小,向关键字6左子树查找;关键字变为4,关键字5比4大,向关键字4右子树查找;关键字变为5,与目标关键字相同,结束查找,返回当前结点指针。

当然,我们也可以采用循环展开递归,用迭代完成搜索。

public static BSTreeNode IterativeTreeSearch(BSTreeNode x, int k){while(x != null && k != x.key){if(k < x.key) x = x.left;else x = x.right;}return x;
}

查询二叉树——最大关键字元素和最小关键字元素

从树根开始,沿着left孩子指针,直到遇到一个NULL,就可以查找到以结点x为根的子树中最小的元素的指针。
public static BSTreeNode TreeMininum(BSTreeNode x){while(x != null && x.left != null)x = x.left;return x;
}

若x为NULL,则不存在最小关键字。若x没有左子树,那么由于x中的每个关键字都至少大于或等于x.key,则以x为根的子树中的最小关键字是x.key。若x有左子树,那么由于其右子树中没有关键字小于x.key,且在左子树中每个关键字都不大于x.key,则以x为跟的子树中的最小关键字一定在以x.left为根的子树中。

此外,TreeMaxinum与TreeMininum代码对称:
public static BSTreeNode TreeMaxinum(BSTreeNode x){while(x != null && x.right != null)x = x.right;return x;
}

这两个操作在高度为h的树上均能在O(h)的时间内完成。

查询二叉树——后继和前驱

给定一棵二叉搜索树中的一个结点,有时候需要按照中序遍历的次序查找它后继。如果所有的关键字互不相同,则一个结点x的后继是大于x.key的最小关键字的结点。
public static BSTreeNode TreeSuccessor(BSTreeNode x){if(x.right != null)return TreeMininum(x.right);BSTreeNode y = x.p;while(y != null && x == y.right){x = y;y = y.p;}return y;
}

TreeSuccessor分为两种情况:如果结点x的右子树非空,那么x的后继恰是x右子树中的最左结点;如果结点x的右子树为空且y是它的后继,那么y就是x的有左孩子的最底层祖先,而且这个y的左孩子也是x的一个祖先。如结点5的后继是6,因为6是5的祖先,并且6的左孩子4也是5的祖先。

在一棵高度为h的树上,TreeSuccessor的运行时间为O(h),因为该过程或者遵从一条简单路径沿树向上或者遵从简单路径沿树向下。

TreePredecessor与TreeSuccessor是对称的,其运行时间也为O(h)。

public static BSTreeNode TreePredecessor(BSTreeNode x){if(x.left != null)return TreeMaxinum(x.right);BSTreeNode y = x.p;while(y != null && x == y.left){x = y;y = y.p;}return y;
}

插入和删除

插入和删除操作会引起由二叉搜索树表示的动态集合的变化,一定要通过修改数据结构来反映这种变化,但是修改要保持二叉搜索树性质的成立。

插入

要将值v插入二叉搜索树中,可以创建新的结点z,其中z.key=v,z.left=NULL,z.right=NULL,通过调用过程TreeInsert,修改搜索树T和z的某些属性,把z插入树中相应的位置。

public void TreeInsert(BSTreeNode z){//this为BSTreeBSTreeNode y = null, x = this.root;while(x != null){y = x;if(z.key < x.key) x= x.left;else x = x.right;}z.p = y;if(y == null) this.root = z;           //tree is emptyelse if(z.key < y.key)y.left = z;else y.right = z;
}

TreeInsert从树根开始,指针x记录一条向下的简单路径,并查找要替换的输入项z的叶子结点。该过程保持遍历指针y作为x的双亲。在循环时,根据z.key和x.key的比较结果决定向左移或向右移,直到x变为NULL,这个NULL的位置就是输入项z要放置的位置,此时根据y的指针可以获得该位置的双亲节点,从而把z放置到当前位置。

TreeInsert在高度为h的树上的运行时间为O(h)。

删除

从二叉搜索树T中删除一个结点z分为三种基本情况:

1.如果z没有孩子节点,那么就简单的将它删除,并修改它的父结点,用NULL作为孩子替换z;
2.如果z只有一个孩子,那么将这个孩子提升到树中z的位置上,并修改z的父结点,用z的孩子来替换z;
3.如果z有两个孩子,那么找z的后继y(一定在z的右子树中),并让y占据树中z的位置。Z的原来右子树部分变成y的新的右子树,并且z的左子树成为y的新的左子树。(为什么找z的后继:根据中序遍历,z的后继一定是比z大的最小数字,故用这个数字替换z后,z左子树的数字肯定比它小,右子树的数字肯定比它大,这样就保持了搜索树的特性)

对以上情况具体分析来讲,又可以分为以下四种具体的情况:

1.如果z没有左孩子,那么用其右孩子来替换z,即使这个右孩子是NULL也可以用来替换。当z的右孩子是NULL时,这种情况可以归为z没有孩子节点的情况。当z的右孩子不为NULL时,这种情况就是z仅有一个孩子结点的情况,这个孩子就是其右孩子;

2.如果z仅有一个孩子,且为左孩子,那么就用z的左孩子替代z;

3.否则,z既有左孩子又有右孩子。我们要查找z的后继y,这个后继位于z的右子树中并且是没有左孩子的结点。要把这个y移出原来的位置,并替代z。

4.如果y是z的右孩子,那么用y替换z,并仅留下y的右孩子。

5.否则,y位于z的右子树中但并不是z的右孩子,这种情况下,先用y的右孩子替换y,然后再用y替换z。这种情况中,y是z的右子树下的最小元素(调用TreeMininum获取),然后用y的右子树x替换y,并且用y作为z的右子树的根,这样整体上来看就将这种情况变为上面的四种情况了,紧接着用y替换z即可。

private void TransPlant(BSTreeNode u, BSTreeNode v){// v replace uif(u.p == null) this.root = v;else if(u == u.p.left) u.p.left = v;else u.p.right = v;if(v != null)v.p = u.p;
}
public void TreeDelete(BSTreeNode z){if(z.left == null)TransPlant(z, z.right);else if(z.right == null)TransPlant(z, z.left);else{BSTreeNode y = TreeMininum(z.right);if(y.p != z){//不是右孩子TransPlant(y, y.right);y.right = z.right;y.right.p = y;}TransPlant(z, y);//y是z的右孩子y.left = z.left;y.left.p = y;}
}

其中,TransPlant表示用一棵以v为根的子树来替换一棵以u为根的子树,替换后结点u的双亲就变为结点v的双亲,并且最后v成为u的双亲的相应孩子。

TreeDelete分为三种情况:z没有左孩子,用z.right这个子树替换z;z没有右孩子,用z.right这棵子树替换z;z有两个孩子的情况,用y表示z中序遍历时的后继(也就是比z小的最大数字),这种情况分为两种类型,1. y是z的右孩子,这种情况下y必定是没有左孩子,可以用y直接替换z、2. Y不是z的右孩子,这种情况下,y是z的右子树中最左下的子树,并且没有左孩子,此时用y的右孩子替换y,并且将z的右子树作为y的右孩子(保证y左孩子为NULL)此时情况就转化为这种情况下的1,此时就可以直接用y这棵子树替换z。

TreeDelete中除了调用TreeMininum之外,都只花费常数时间,因此,在一棵高度为h的树上,TreeDelete的运行时间为O(h)。

故,在一棵高度为h的二叉搜索树上,实现动态集合操作Insert和Delete的运行时间均为O(h)。

附完整代码:

class BSTreeNode{int key;Object value;//dataBSTreeNode left;BSTreeNode right;BSTreeNode p;//parentpublic BSTreeNode(int key){this.key = key;}
}public class BSTree {public static void InorderTreeWalk(BSTreeNode x){if(x != null){InorderTreeWalk(x.left);System.out.println(x.key);InorderTreeWalk(x.right);}}//查找public static BSTreeNode TreeSearch(BSTreeNode x, int k){if(x == null || k == x.key){return x;}if(k < x.key)return TreeSearch(x.left, k);else return TreeSearch(x.right, k);}public static BSTreeNode IterativeTreeSearch(BSTreeNode x, int k){while(x != null && k != x.key){if(k < x.key) x = x.left;else x = x.right;}return x;}public static BSTreeNode TreeMininum(BSTreeNode x){while(x != null && x.left != null)x = x.left;return x;}public static BSTreeNode TreeMaxinum(BSTreeNode x){while(x != null && x.right != null)x = x.right;return x;}//后继、前驱public static BSTreeNode TreeSuccessor(BSTreeNode x){if(x.right != null)return TreeMininum(x.right);BSTreeNode y = x.p;while(y != null && x == y.right){x = y;y = y.p;}return y;}public static BSTreeNode TreePredecessor(BSTreeNode x){if(x.left != null)return TreeMaxinum(x.right);BSTreeNode y = x.p;while(y != null && x == y.left){x = y;y = y.p;}return y;}private BSTreeNode root;//插入public void TreeInsert(BSTreeNode z){//this为BSTreeBSTreeNode y = null, x = this.root;while(x != null){y = x;if(z.key < x.key) x= x.left;else x = x.right;}z.p = y;if(y == null) this.root = z;           //tree was emptyelse if(z.key < y.key)y.left = z;else y.right = z;}//删除private void TransPlant(BSTreeNode u, BSTreeNode v){// v replace uif(u.p == null) this.root = v;else if(u == u.p.left) u.p.left = v;else u.p.right = v;if(v != null)v.p = u.p;}public void TreeDelete(BSTreeNode z){if(z.left == null)TransPlant(z, z.right);else if(z.right == null)TransPlant(z, z.left);else{BSTreeNode y = TreeMininum(z.right);if(y.p != z){//不是右孩子TransPlant(y, y.right);y.right = z.right;y.right.p = y;}TransPlant(z, y);//y是z的右孩子y.left = z.left;y.left.p = y;}}public static void main(String[] args) {BSTree T = new BSTree();int a[] = {12, 5, 2, 9, 18, 15, 13, 17, 19};for(int i : a){BSTreeNode node = new BSTreeNode(i);T.TreeInsert(node);}InorderTreeWalk(T.root);}
}

Binary Search Tree(二叉搜索树、二叉查找树、二叉排序树)相关推荐

  1. 【基础知识】 之 Binary Search Tree 二叉搜索树

    前言 这个系列是毕业找工作的复习笔记,希望可以和广大正准备毕业的童鞋一起打牢基础,迎接各种笔试--为了应付中英文笔试,关键词都用英文进行标注,这样就不怕面对英文题目了.之所以开始这一系列是因为之前在参 ...

  2. PAT甲级——1099 Build A Binary Search Tree (二叉搜索树)

    本文同步发布在CSDN:https://blog.csdn.net/weixin_44385565/article/details/90701125 1099 Build A Binary Searc ...

  3. 二叉搜索树(二叉排序树)

    全部数据结构.算法及应用课内模板请点击:https://blog.csdn.net/weixin_44077863/article/details/101691360 二叉搜索树(二叉排序树)(bin ...

  4. 数组模拟二叉搜索树(二叉排序树)

    文章目录 1. 二叉搜索树的定义 2. 二叉搜索树经典模板 2.1 插入操作(建树操作) 2.2 删除操作 2.3 查询二叉搜索树中值为 w 的前驱/后继数值 3. 经典例题 1. 二叉搜索树的定义 ...

  5. 数据结构二叉排序树建立_数据结构101什么是二叉搜索树

    数据结构二叉排序树建立 In everyday life, we need to find things or make decisions, and one way to make that pro ...

  6. C++数据结构和算法2 栈 双端/队列 冒泡选择插入归并快排 二三分查找 二叉树 二叉搜索树 贪婪 分治 动态规划

    C++数据结构和算法2 栈 双端/队列 冒泡选择插入归并快排 二三分查找 二叉树 二叉搜索树 贪婪 分治 动态规划 博文末尾支持二维码赞赏哦 _ github 章3 Stack栈 和 队列Queue= ...

  7. B-树(B-Tree)与二叉搜索树(BST):讲讲数据库和文件系统背后的原理(读写比较大块数据的存储系统数据结构与算法原理)...

    人类总喜欢发明创造一些新名词(比如说,简写/缩写/简称什么的),并通过这些名词把人群分成了三六九等.弄到最后,把自己都绕晕了. 你看,首先就是,B树,不要与Binary tree或B+tree混淆. ...

  8. 什么是m叉树_树、二叉树、二叉搜索树的实现和特性

    ❝ 点赞再看,养成习惯,微信搜一搜[一角钱小助手]关注更多原创技术文章. 回复「文章」获取系列完整文章,本文 org_hejianhui/JavaStudy 已经收录,欢迎Star. ❞ 前言 本篇先 ...

  9. b+树时间复杂度_深入理解数据库系统之存储存引擎(二叉搜索树)

    B树是数据库存储引擎使用的最多的存储结构之一.许多开源数据库系统也都大量使B用树作为存储结构,多年来已经证明它们能够胜任大多数使用场景. 早在1971年鲁道夫·拜尔(Rudolph Bayer)和爱德 ...

  10. 数据结构:二叉搜索树的增删查改

    二叉搜索树的增删查改 二叉搜索树(Binary Search Tree) 基本操作之查找(Update) 基本操作之修改(Update) 基本操作之增加(Create) 基本操作之删除(Delete) ...

最新文章

  1. Android中ContentProvider组件数据共享
  2. Docker容器学习
  3. PostgreSQL的clog—从事务回滚速度谈起
  4. POJ2146 Confusing Login Names [最小字符串编辑距离]
  5. 什么叫做支路_家庭电路用什么开关?不同的开关有什么用?主开关用漏保更好吗?...
  6. ChannelHandler揭秘(Netty源码死磕5)
  7. SpringBoot+Vue项目校园闲置物品交易系统
  8. pytorch的dataset用法详解
  9. 软件测试详细的基本流程
  10. SpringCloud之Zuul微服务网关 什么是Zuul微服务网关?
  11. 计算机网络被限速,电脑网速被限制怎么办
  12. 用html制作四行四列的表格,HTML表格元素
  13. 3D劲舞游戏 Dance Mixer 简体中文汉化版+常见问题(更新1.1补丁 汉化)
  14. redis之db(数据库)
  15. VUE PC端和移动端适配
  16. 句法引导的机器阅读理解
  17. 更改计算机休眠,win 7 无法设置自动休眠时间
  18. android imageview 锯齿,[置顶] android 自定义圆角ImageView以及锯齿的处理
  19. 医院信息管理系统论文java_毕业论文-基于java的医院门诊信息管理系统设计与实现...
  20. 服务器4个网口只显示2个,服务器4个网口的作用

热门文章

  1. 2017 java 面试大全
  2. Android 仿网易云音乐App
  3. simulink-他励直流电动机的直接启动仿真
  4. java火焰纹章攻略女神之剑_火焰纹章晓之女神图文攻略(4)
  5. 二进制数字的表示方法
  6. VR虚拟现实心理脱敏训练系统整体解决方案
  7. MMDetection 2.0安装笔记
  8. mysql 云端连接_云服务器远程连接mysql数据库
  9. CSS如何写出圆圈1(详细)
  10. 无需下载软件pdf转jpg格式