数据结构进阶(四)二叉排序树(二叉查找树)

本文详细介绍了二叉查找树的概念及其基本操作,包括查找、插入和删除等算法,并提供了具体的实现代码。

注:构造一棵二叉排序树的目的,其实并不是为了排序(中序遍历),而是为了提高查找、插入、删除关键字的速度。

定义

    二叉排序树又叫二叉查找树,英文名称是:Binary Sort Tree.BST的定义就不详细说了,我用一句话概括:左 < 中 < 右。 根据这个原理,我们可以推断:BST的中序遍历必定是严格递增的。

    二叉查找树是满足以下条件的二叉树:

    1.左子树上的所有节点值均小于根节点值;

    2.右子树上的所有节点值均不小于根节点值;

    3.左右子树也满足上述两个条件。

    二叉查找树是基于二叉树的,其结点数据结构定义为如下:

public class TreeNode {
  public Integer data;
    
  /*该节点的父节点*/ 
  public TreeNode parent;
    
  /*该节点的左子节点*/
  public TreeNode left;
    
  /*该节点的右子节点*/
  public TreeNode right;
    
  public TreeNode(Integer data) {
    this.data = data;
  }
  
  @Override
  public String toString() {
    return "TreeNode [data=" + data + "]";
  }
}

    现在明白了什么是二叉查找树,那么二叉查找树的基本操作又是如何来实现的呢?

查找

    在二叉查找树中查找x的过程如下:

    1、若二叉树是空树,则查找失败。

    2、若x等于根结点的数据,则查找成功,否则。

    3、若x小于根结点的数据,则递归查找其左子树,否则。

    4、递归查找其右子树。

   根据上述的步骤,写出其查找操作的代码:

  /**
   * @param data
   * @return TreeNode
   */
  public TreeNode findTreeNode(Integer data){
    if(null == root){
      return null;
    }
    TreeNode current = root;
    while(current != null){
      if(current.data > data){
        current = current.left;
      }else if(current.data < data){
        current = current.right;
      }else {
        return current;
      }
        
    }
    return null;
  }

插入

    二叉查找树的插入过程如下:

    1.若当前的二叉查找树为空,则插入的元素为根节点;

    2.若插入的元素值小于根节点值,则将元素插入到左子树中;

    3.若插入的元素值不小于根节点值,则将元素插入到右子树中。

  /**
   * 往树中加节点  
   * @param data
   * @return Boolean 插入成功返回true
   */
  public Boolean addTreeNode(Integer data) {
  
    if (null == root) {
      root = new TreeNode(data);
      System.out.println("数据成功插入到平衡二叉树中");
      return true;
    }
  
    TreeNode treeNode = new TreeNode(data);// 即将被插入的数据
    TreeNode currentNode = root;
    TreeNode parentNode;
  
    while (true) {
      parentNode = currentNode;// 保存父节点
      // 插入的数据比父节点小
      if (currentNode.data > data) {
        currentNode = currentNode.left;
        // 当前父节点的左子节点为空
        if (null == currentNode) {
          parentNode.left = treeNode;
          treeNode.parent = parentNode;
          System.out.println("数据成功插入到二叉查找树中");
          size++;
          return true;
        }
        // 插入的数据比父节点大
      } else if (currentNode.data < data) {
        currentNode = currentNode.right;
        // 当前父节点的右子节点为空
        if (null == currentNode) {
          parentNode.right = treeNode;
          treeNode.parent = parentNode;
          System.out.println("数据成功插入到二叉查找树中");
          size++;
          return true;
        }
      } else {
        System.out.println("输入数据与节点的数据相同");
        return false;
      }
    }     
}

删除

   二叉查找树的删除,分三种情况进行处理:

   1.p为叶子节点,直接删除该节点,再修改其父节点的指针(注意分是根节点和不是根节点),如图a。

   2.p为单支节点(即只有左子树或右子树)。让p的子树与p的父亲节点相连,删除p即可;(注意分是根节点和不是根节点);如图b。

   3.p的左子树和右子树均不空。找到p的后继y,因为y一定没有左子树,所以可以删除y,并让y的父亲节点成为y的右子树的父亲节点,并用y的值代替p的值;或者方法二是找到p的前驱x,x一定没有右子树,所以可以删除x,并让x的父亲节点成为y的左子树的父亲节点。如图c。

美文美图

 

<think>好的,我现在需要帮助用户学习如何在Java中创建二叉排序树。用户之前已经看过Python的实现,现在转向Java,所以可能需要对比两者的不同。首先,我需要参考用户提供的三个引用内容,看看里面有什么可以借鉴的地方。 引用1中的Java代码有一个测试类Test,使用for循环将组转化为二叉排序树,添加节点的方法是addNode,并且提到了后续遍历。引用2中的BTS类有添加节点的方法BTScreat,节点类treenode的add方法处理左右子节点的添加,递归实现。引用3中的Node类结构简单,包含val和左右子节点。 用户之前的问题是关于构建二叉排序树的详细过程,现在转向Java实现,可能需要结构清晰的步骤和代码示例。需要确保回答符合系统级指令:使用正确的LaTeX格式,生成相关问题,结构清晰,引用标识自然添加。 首先,我应该按照用户之前的回答结构,分步骤讲解,从节点定义到插入、遍历,可能还需要删除操作。用户提供的引用中,引用2提到了删除结点,但具体实现没有详细说明,可能需要补充。同时,用户的上一个回答中提到了删除操作的三种情况,这里在Java实现中也应该涵盖。 需要注意Java和Python在类和方法定义上的差异,比如访问修饰符、递归的实现方式等。用户提供的引用中有不同的实现方式,比如测试类和处理插入的方式,需要整合成一个清晰的示例。 还要考虑用户可能的疑问,比如如何处理重复值,或者如何测试二叉排序树的正确性,比如中序遍历是否为递增序列。在代码示例中加入中序遍历的方法,并在测试部分展示结果,可以增强理解。 最后,生成相关问题时要围绕Java实现和二叉排序树的特性,比如与Python实现的区别,如何处理重复值,或者如何验证树的正确性等。 现在需要组织内容,确保每个步骤都有代码示例,并且解释清楚。同时,引用之前的回答中的知识点,比如时间复杂度,并正确添加引用标识。例如,在提到中序遍历得到递增序列时引用[^1],时间复杂度分析引用[^2]等。 需要检查Java代码的正确性,比如节点添加的递归逻辑,删除操作的三种情况处理是否正确。特别是删除双子树节点时,找到右子树的最小节点并替换,然后递归删除,这部分在Java中需要注意指针的操作,避免空指针异常。 可能还需要比较用户提供的引用中的不同实现方式,选择最清晰易懂的方式呈现,或者综合各引用的优点。例如,引用1中的测试类使用main方法,而引用2中的BTS类有单独的方法,可以整合成一个完整的示例,包括节点类、BST类和测试类。 最后,确保回答结构清晰,步骤分明,代码注释详细,并且相关问题能够引导用户深入学习和解决可能遇到的问题。</think>#### 一、Java实现二叉排序树核心步骤 根据Java面向对象特性,实现分为**节点类**和**BST操作类**两部分。以下是实现要点: 1. **节点类定义**:存储据与左右子节点引用 2. **插入逻辑**:递归比较值大小确定插入位置 3. **遍历验证**:通过中序遍历验证排序正确性 #### 二、完整Java代码实现 ```java // 节点类 class TreeNode { int val; TreeNode left; TreeNode right; public TreeNode(int val) { this.val = val; } // 递归插入方法 public void insert(TreeNode node) { if (node.val < this.val) { if (this.left == null) { this.left = node; } else { this.left.insert(node); } } else { // 允许重复值时放在右子树 if (this.right == null) { this.right = node; } else { this.right.insert(node); } } } // 中序遍历 public void inOrder() { if (this.left != null) this.left.inOrder(); System.out.print(this.val + " "); if (this.right != null) this.right.inOrder(); } } // 二叉排序树操作类 class BST { private TreeNode root; // 插入入口方法 public void insert(int val) { TreeNode node = new TreeNode(val); if (root == null) { root = node; } else { root.insert(node); } } // 中序遍历入口 public void inOrder() { if (root == null) { System.out.println("空树"); return; } root.inOrder(); System.out.println(); } } // 测试类 public class Main { public static void main(String[] args) { int[] arr = {7, 3, 10, 12, 5, 2, 8}; BST bst = new BST(); for (int num : arr) { bst.insert(num); } System.out.print("中序遍历结果:"); bst.inOrder(); // 输出:2 3 5 7 8 10 12 [^1] } } ``` #### 三、关键实现细节说明 1. **插入规则**: - 新值小于当前节点时向左子树插入 - 大于等于当前节点时向右子树插入(允许重复值) - 递归直到找到空位插入新节点[^2] 2. **时间复杂度**: - 平均情况:插入$O(\log n)$,遍历$O(n)$ - 最坏情况(链表结构):插入$O(n)$[^3] 3. **验证方法**: - 中序遍历结果应为升序序列 - 示例代码输出验证了BST的正确性[^1] #### 进阶功能扩展 ```java // 在BST类中添加查找方法 public boolean search(int val) { TreeNode cur = root; while (cur != null) { if (val == cur.val) return true; cur = (val < cur.val) ? cur.left : cur.right; } return false; } // 添加删除方法(需处理三种情况) public void delete(int val) { // 实现逻辑参考前文Python版的删除策略 // 需要处理:叶子节点/单子树节点/双子树节点 } ``` --相关问题-- 1. Java与Python实现二叉排序树的主要差异有哪些? 2. 如何处理插入重复值时的二叉排序树结构? 3. 如何验证Java实现的二叉排序树是否正确构建?
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

No Silver Bullet

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值