二叉排序树

  • 为什么需要二叉排序树?
  • 什么是二叉排序树(BST)?
  • 二叉排序树代码实现
    • 二叉排序树添加节点
    • 二叉排序树查找节点
    • 二叉排序树删除节点
      • 搜索父节点
      • 删除一颗树中最小的节点并获取值
      • 删除没有子节点的节点(删除叶子结点)
      • 删除有一个子节点的节点
      • 删除有两个子节点的节点
      • 二叉排序树删除节点完整代码
  • 二叉排序树完整代码
    • 二叉排序树类 BinarySortTree
    • 二叉排序树节点类 Node
    • 测试类 TestBinaryTree

为什么需要二叉排序树?

假设有一组数字,{7,3,10,12,5,1,9},面对这么一组数字,我们有很多种方式来存储他们。

例如线性结构不排序的顺序存储结构

对这种数据结构来说,它查找某个元素十分的困难,需要从头到尾挨个去寻找。

线性结构排序的顺序存储结构

哪怕是排序过后的该种数据结构,通过二分查找,它的查找效率会变高,但是它的插入元素与删除元素都比较困难,需要移动大量的元素达到添加与删除的效果。查找第一个值 1 与 最后一个值 12,效率相差过大,整体来说,时间效率并不稳定

想要达到插入删除方便的效果,我们会立马想到链式存储
但是对链式存储来说,无论是否排序过,它的查找依旧十分困难,需要遍历整个树达到查找的效果。

此时,为了达到查找简易,并且插入删除都方便的效果,引入一个新的数据结构:二叉查找树

什么是二叉排序树(BST)?

二叉排序树(BST),也叫二叉查找树,二叉搜索树。对于二叉树中的任意一个非叶子节点,要求左子节点比当前节点值小,右子节点比当前节点值大。 如果是一颗空树,也可认为是二叉排序树。

由以上给出的一组数据,我们可以构造成如下所示的二叉查找树。

二叉查找树的查找十分方便,例如查找图中的 12,只需经过 2 次即可。并且二叉查找树的添加与删除也十分方便,只需查找到相应的结点后,添加或删除元素即可。

二叉排序树代码实现

二叉排序树添加节点

// 二叉排序树类
public class BinarySortTree {Node root;// 向二叉排序树中添加节点public void add(Node node){if (root == null){ // 如果是一颗空树root = node; // 则添加节点为根结点}else{root.add(node);}}}// 二叉排序树的根节点类
class Node{int value;Node left;Node right;public Node(int value){this.value = value;}// 向二叉排序树中添加节点public void add(Node node) {if (node == null){return;}// 判断传入的节点的值比当前子树的根结点的值大还是小if (value > node.value){ // 如果添加的节点比当前节点小,往左if (left == null){ // 如果左子树为空,直接令左子树为添加的节点left = node;}else{ // 如果左子树不为空, 递归添加left.add(node);}}else{ // 如果添加的节点比当前节点大,往右if (right == null){ // 如果右子树为空,直接令右子树为添加的节点right = node;}else{ // 如果右子树不为空,递归添加right.add(node);}}}}

根据 {7, 3, 10, 12, 5, 1, 9} 建立一颗二叉排序树:

public static void main(String[] args) {int [] arr = new int[] {7, 3, 10, 12, 5, 1, 9};BinarySortTree bst = new BinarySortTree();for (int i : arr) {bst.add(new Node(i));}
}

二叉排序树查找节点

/*二叉排序树类中的查找方法*/
public Node search(int value){if (root == null){return null;}else{return root.search(value);}
}
---------------------------------------------------------------------
/*二叉排序树节点类中的查找方法*/
// 查找节点
public Node search(int value) {if (this.value == value){ return this;}else if(value < this.value){ // 查找的值比当前值小,往左if (left == null){ // 如果左子树为空,return nullreturn null;}return left.search(value); // 左子树不为空,递归查找}else { // 查找的值比当前值大,往右if (right == null){ // 如果右子树为空,return nullreturn null;}return right.search(value); // 右子树不为空,递归查找}
}

在上面创建的二叉排序树中,查找节点5(存在)与节点20(不存在):

public static void main(String[] args) {int [] arr = new int[] {7, 3, 10, 12, 5, 1, 9};BinarySortTree bst = new BinarySortTree();for (int i : arr) {bst.add(new Node(i));}System.out.print("查找值为5的节点:");Node node = bst.search(5);System.out.println(node.value);System.out.print("查找值为20的节点:");Node node2 = bst.search(20);System.out.println(node2);
}

运行效果:输出了查找到的节点的值,值为20的节点并不存在。

查找值为5的节点:5
查找值为20的节点:null

二叉排序树删除节点

对于这么一个二叉树,删除它的节点可以分为三种情况:

  • 删除没有子节点的节点(删除叶子结点)
  • 删除有两个子节点的节点
  • 删除有两个子节点的节点

在讨论这三种情况前,先来看两个方法:搜索父节点删除一颗树中最小的节点并获取值,这两个方法将会在删除节点中用到。

搜索父节点

// 二叉排序树类中的 searchParent() 方法
// 搜索父节点
public Node searchParent(int value){if (root == null){ // 如果为空树则return nullreturn null;}else{return root.searchParent(value);}
}
---------------------------------------------------------------------
// 二叉排序树的节点类中的 searchParent() 方法
// 搜索父节点
public Node searchParent(int value) {if ((this.left != null && this.left.value == value) || (this.right != null && this.right.value == value)){return this;}else {if (this.value > value && this.left!= null){return this.left.searchParent(value);}else if(this.value < value && this.right != null){return this.right.searchParent(value);}return null;}
}

删除一颗树中最小的节点并获取值

// 删除一颗树中最小的节点
private int deleteMin(Node node) {Node target = node;while (target.left != null){ // 递归找到最小的节点target = target.left;}delete(target.value);return target.value;
}

然后我们开始讨论二叉排序树中三种删除节点的情况:

删除没有子节点的节点(删除叶子结点)


例如,假设我们要删除上图中值为1的节点,我们只需要令3的左子树为 null 即可。

删除后二叉树如下图:

删除叶子节点的过程

  • 如果要删除的叶子节点是父节点的左节点,则令父节点的左子树为 null;
  • 如果要删除的叶子结点是父节点的右节点,则令父节点的右子树为 null。
Node target = search(value); // 找到要删除节点节点
if (target == null){ // 如果没有这个节点return;
}
Node parent = searchParent(value); // 找到要删除节点的父节点
/*要删除的节点是叶子结点*/
if (target.left == null && target.right == null){if (parent.left.value == value){  // 要删除的节点是父节点的左子节点parent.left = null;}else{ // 要删除的节点是父节点的右子节点parent.right = null;}
}

删除有一个子节点的节点


例如,我们删除了值为1的节点后,继续删除值为3的节点(如上图),我们需要将3的子节点替换到3的位置。

删除后二叉树如下图:

删除有一个子节点的过程:

  • 判断要删除的节点是父节点的左子节点,还是右子节点;
  • 如果是父节点的左子节点,令要删除的节点的子节点为父节点的左子节点;
  • 如果是父节点的右子节点,令要删除的节点的子节点为父节点的右子节点;
if (target.left != null){ // 要删除的节点有左子节点if (parent.left.value == value){  // 要删除的节点是父节点的左子节点parent.left = target.left;}else { // 要删除的节点是父节点的右子节点parent.right = target.left;}
}else{ // 要删除的节点有右子节点if (parent.left.value == value){ // 要删除的节点是父节点的左子节点parent.left = target.right;}else{ // 要删除的节点是父节点的右子节点parent.right = target.right;}
}

删除有两个子节点的节点


例如,我们要删除上图中值为7的节点,我们需要在7的右子树中找到最小的值放到7节点的位置,因此我们将9替换到7的位置。

删除后二叉树如下图:

删除有两个子节点的节点的过程:

  • 首先删除右子树中值最小的节点并且获取到该节点的值
  • 然后用上面获取的值替换要删除节点的值
if (target.left != null && target.right != null){int min = deleteMin(target.right); // 删除右子树中值最小的节点,并获取到该节点的值target.value = min; // 替换目标节点中的值

二叉排序树删除节点完整代码

// 删除节点
public void delete(int value){if (root == null){return;}else{Node target = search(value); // 找到这个节点if (target == null){ // 如果没有这个节点return;}Node parent = searchParent(value); // 找到他的父节点/*要删除的节点是叶子结点*/if (target.left == null && target.right == null){if (parent.left.value == value){  // 要删除的节点是父节点的左子节点parent.left = null;}else{ // 要删除的节点是父节点的右子节点parent.right = null;}/*要删除的节点有两个子节点*/}else if (target.left != null && target.right != null){int min = deleteMin(target.right); // 删除右子树中值最小的节点,并获取到该节点的值target.value = min; // 替换目标节点中的值/*要删除的节点就1个左子节点或右子节点*/}else{if (target.left != null){ // 有左子节点if (parent.left.value == value){  // 要删除的节点是父节点的左子节点parent.left = target.left;}else { // 要删除的节点是父节点的右子节点parent.right = target.left;}}else{ // 有右子节点if (parent.left.value == value){ // 要删除的节点是父节点的左子节点parent.left = target.right;}else{ // 要删除的节点是父节点的右子节点parent.right = target.right;}}}}
}

测试案例:

System.out.print("二叉排序树的中序遍历:");
bst.midShow();
System.out.print("\n删除节点1后的中序遍历:"); // 删除没有子节点的节点(叶子节点)
bst.delete(1);
bst.midShow();
System.out.print("\n删除节点3后的中序遍历:"); // 删除有一个子节点的节点
bst.delete(3);
bst.midShow();
System.out.print("\n删除节点7后的中序遍历:"); // 删除有两个子节点的节点
bst.delete(7); // 删除有两个子节点的节点
bst.midShow();

运行效果:

二叉排序树的中序遍历:1 3 5 7 9 10 12
删除节点1后的中序遍历:3 5 7 9 10 12
删除节点3后的中序遍历:5 7 9 10 12
删除节点7后的中序遍历:5 9 10 12

二叉排序树完整代码

二叉排序树类 BinarySortTree

package com.yusael.BinarySortTree;public class BinarySortTree {Node root;// 向二叉排序树中添加节点public void add(Node node){if (root == null){ // 如果是一颗空树root = node; // 则添加结点为根结点}else{root.add(node);}}// 中序遍历public void midShow(){if (root == null){ // 空树则直接returnreturn;}root.midShow();}// 查找节点public Node search(int value){if (root == null){ // 空树则直接returnreturn null;}else{return root.search(value);}}// 删除节点public void delete(int value){if (root == null){return;}else{Node target = search(value); // 找到这个节点if (target == null){ // 如果没有这个节点return;}Node parent = searchParent(value); // 找到他的父节点/*要删除的节点是叶子结点*/if (target.left == null && target.right == null){if (parent.left.value == value){  // 要删除的节点是父节点的左子节点parent.left = null;}else{ // 要删除的节点是父节点的右子节点parent.right = null;}/*要删除的节点有两个子节点*/}else if (target.left != null && target.right != null){int min = deleteMin(target.right); // 删除右子树中值最小的节点,并获取到该节点的值target.value = min; // 替换目标节点中的值/*要删除的节点就1个左子节点或右子节点*/}else{if (target.left != null){ // 有左子节点if (parent.left.value == value){  // 要删除的节点是父节点的左子节点parent.left = target.left;}else { // 要删除的节点是父节点的右子节点parent.right = target.left;}}else{ // 有右子节点if (parent.left.value == value){ // 要删除的节点是父节点的左子节点parent.left = target.right;}else{ // 要删除的节点是父节点的右子节点parent.right = target.right;}}}}}// 删除一颗树中最小的节点private int deleteMin(Node node) {Node target = node;while (target.left != null){ // 递归找到最小的节点target = target.left;}delete(target.value);return target.value;}// 搜索父节点public Node searchParent(int value){if (root == null){ // 如果为空树则return nullreturn null;}else{return root.searchParent(value);}}}

二叉排序树节点类 Node

package com.yusael.BinarySortTree;public class Node {int value;Node left;Node right;public Node(int value){this.value = value;}// 向子树中添加节点public void add(Node node) {if (node == null){ // 传入空结点,什么也不做return;}// 判断传入的节点的值比当前子树的根结点的值大还是小if (node.value < this.value){ // 要添加的结点比当前节点的值更小,往左if (left == null){ // 如果左节点为空,则直接添加left = node;}else{ // 如果不为空,递归添加left.add(node);}}else{if (right == null) { // 如果右节点为空,则直接添加right = node;}else{ // 如果不为空,递归添加right.add(node);}}}// 中序遍历public void midShow() {if (this.left != null){left.midShow();}System.out.print(value + " ");if (this.right != null){right.midShow();}}// 查找节点public Node search(int value) {if (this.value == value){return this;}else if(value < this.value){ // 查找的值比当前值小,往左if (left == null){ // 如果左子树为空,return nullreturn null;}return left.search(value); // 左子树不为空,递归查找}else { // 查找的值比当前值大,往右if (right == null){ // 如果右子树为空,return nullreturn null;}return right.search(value); // 右子树不为空,递归查找}}// 搜索父节点public Node searchParent(int value) {if ((this.left != null && this.left.value == value) || (this.right != null && this.right.value == value)){return this;}else {if (this.value > value && this.left!= null){return this.left.searchParent(value);}else if(this.value < value && this.right != null){return this.right.searchParent(value);}return null;}}
}

测试类 TestBinaryTree

package com.yusael.BinarySortTree;public class TestBinaryTree {public static void main(String[] args) {int [] arr = new int[] {7, 3, 10, 12, 5, 1, 9};BinarySortTree bst = new BinarySortTree(); // 创建一颗二叉排序树for (int i : arr){// 循环添加bst.add(new Node(i));}System.out.print("二叉排序树的中序遍历:");bst.midShow();System.out.print("\n查找值为5的节点:");Node node = bst.search(5);System.out.println(node.value);System.out.print("查找值为20的节点:");Node node1 = bst.search(20);System.out.println(node1);// 查找父节点System.out.print("查找12的父节点:");Node node2 = bst.searchParent(12);System.out.println(node2.value);System.out.print("删除节点1后的中序遍历:"); // 删除叶子节点bst.delete(1);bst.midShow();System.out.print("\n删除节点3后的中序遍历:"); // 删除有一个子节点的节点bst.delete(3);bst.midShow();System.out.print("\n删除节点7后的中序遍历:"); // 删除有两个子节点的节点bst.delete(7); // 删除有两个子节点的节点bst.midShow();}
}

运行效果:

二叉排序树的中序遍历:1 3 5 7 9 10 12
查找值为5的节点:5
查找值为20的节点:null
查找12的父节点:10
删除节点1后的中序遍历:3 5 7 9 10 12
删除节点3后的中序遍历:5 7 9 10 12
删除节点7后的中序遍历:5 9 10 12

【Java数据结构】二叉排序树相关推荐

  1. java队列_如何彻底搞懂 Java 数据结构?CSDN 博文精选

    作者 | 张振华.Jack 责编 | 郭芮 出品 | CSDN 博客 本文和大家一起来重温<Java数据结构>经典之作. Java数据结构 要理解Java数据结构,必须能清楚何为数据结构? ...

  2. java 固定长度队列_如何彻底搞懂 Java 数据结构?|CSDN 博文精选

    作者 | 张振华.Jack 责编 | 郭芮 出品 | CSDN 博客 本文和大家一起来重温<Java数据结构>经典之作. Java数据结构 要理解Java数据结构,必须能清楚何为数据结构? ...

  3. 一文掌握关于Java数据结构所有知识点(欢迎一起完善)

    在我们学习Java的时候,很多人会面临我不知道继续学什么或者面试会问什么的尴尬情况(我本人之前就很迷茫).所以,我决定通过这个开源平台来帮助一些有需要的人,通过下面的内容,你会掌握系统的Java学习以 ...

  4. 一文掌握关于Java数据结构所有知识点(欢迎一起完善) 1

    摘要: 在我们学习Java的时候,很多人会面临我不知道继续学什么或者面试会问什么的尴尬情况(我本人之前就很迷茫).所以,我决定通过这个开源平台来帮助一些有需要的人,通过下面的内容,你会掌握系统的Jav ...

  5. 如何彻底搞懂 Java 数据结构?|CSDN 博文精选

    作者 | 张振华.Jack 责编 | 郭芮 出品 | CSDN 博客 本文和大家一起来重温<Java数据结构>经典之作. Java数据结构 要理解Java数据结构,必须能清楚何为数据结构? ...

  6. Java数据结构学习笔记

    Java数据结构 枚举(Enumeration)/位集合(BitSet)/向量(Vector)/栈(Stack)/字典(Dictionary)/哈希表(Hashtable)/属性(Properties ...

  7. 使用java数据结构编写代码

    使用java数据结构编写代码 数组数据结构 数组的初始化 public static void main(String[] args) {int[] arrays = new int[5]; // 定 ...

  8. Java数据结构与算法入门

    原文:https://blog.csdn.net/qq_37101453/article/details/80142147 第一部分:Java数据结构 要理解Java数据结构,必须能清楚何为数据结构? ...

  9. 如何理解并掌握 Java 数据结构

    Jack和大家一起来重温<Java数据结构>经典之作. 第一部分:Java数据结构 要理解Java数据结构,必须能清楚何为数据结构? 数据结构: Data_Structure,它是储存数据 ...

  10. 【Java 数据结构】实现一个二叉搜索树

    目录 1.认识二叉搜索树 2.实现一个二叉搜索树 2.1 成员变量 2.2 insert 方法 2.3 search 方法 2.4 remove 方法(重点) 3.二叉搜索树总结 1.认识二叉搜索树 ...

最新文章

  1. ORB 特征检测与匹配
  2. NAR:查询未培养病毒基因组的综合生态和进化框架IMG/VR v3
  3. vue 使用lib-flexable,px2rem 进行移动端适配 但是引入的第三方UI组件 vux 的样式缩小,解决方案...
  4. Altium Designer 隐藏铺铜
  5. navivat11.0.7在linux下面的破解方法
  6. 一键对频对讲机好吗_挑战传统,新型对讲机展现独特一面--极蜂智能网络对讲机...
  7. python求两数之和的命令_数学建模:科学计算Python2小时-Python基础
  8. Spring Cloud 微服务实战系列-Spring Boot再次入门(一)
  9. H5中 video 使用border-radius失效解决方法
  10. 时间序列分析导论书摘:时间预处理-时序变换
  11. 第二阶段团队站立会议02
  12. MAC 迅雷最新版无限重启BUG的解决方法
  13. 超好用的网站克隆工具,就是玩!
  14. php验证电子邮箱格式正确,php判断电子邮件是否正确方法
  15. 怎么将计算机设置为光盘引导模式,通过bios设置从光盘/光驱启动的方法
  16. nginx使用ngx_shared_memory_add和ngx_slab_alloc等在共享内存里创建一棵红黑树
  17. SAP创建供应商及采购订单
  18. 后台拼接字符串加双引号
  19. Pwnginx – a nginx backdoor offering shell
  20. 性能可靠服务器虚拟化,服务器虚拟化下的网络变迁

热门文章

  1. 实体经济的“数字化”是不可逆转的趋势
  2. 删了的微信怎么恢复?
  3. wide-dhcpv6的dhcp6c配置
  4. 限制进程使用的内存大小和CPU时间
  5. aws rds监控慢sql_使用AWS Database迁移服务进行AWS RDS SQL Server迁移
  6. ssma迁移助手_如何使用SQL Server迁移助手(SSMA)和SSIS将MySQL表迁移到SQL Server
  7. sql server 优化_SQL Server中很少有外行优化
  8. ltp-ddt的makefile结构
  9. 数据结构之c++感悟
  10. 课下作业——对正在使用的搜索类软件/输入法进行评价