深入理解java的数据结构之树详解篇二(图解二叉搜索树)

基础

二叉搜索树是一棵空树,或者是具有下列性质的二叉树:

1、若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
2、若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
3、它的左、右子树也分别为二叉排序树。
4、没有键值相等的相关节点。

二叉搜索树作为一种经典的数据结构,它既有链表的快速插入与删除操作的特点,又有数组快速查找的优势;所以应用十分广泛,例如在文件系统和数据库系统一般会采用这种数据结构进行高效率的排序与检索操作。

二叉搜索树定义

BstTreeTest类是二叉搜索树的定义,其中包含了树的节点BstNode的类型root变量,root变量作为二叉搜索树的根节点。

一棵空的二叉搜索树:
在这里插入图片描述
实例代码:

public class BstTreeTest<T extends Comparable<T>>{
// 定义根节点
    private BstNode<T> root;
    
    // 定义无参数构造方法
    public BstTreeTest() {

    }
	// 有参构造方法
    public BstTreeTest(BstNode<T> root) {
        this.root = root;
    }

    // 获取二叉树的根节点
    public BstNode<T> getRoot() {
        return root;
    }

    // 设置二叉树的根节点
    public void setRoot(BstNode<T> root) {
        this.root = root;
    }
  }

节点的定义

BstNode类包含二叉搜索树的几个基本信息:

  • key – 它是关键字,是用来对二叉查找树的节点进行排序的。
  • left – 它指向当前节点的左孩子。
  • right --它指向当前节点的右孩子。
  • parent – 它指向当前节点的父结点。
// 定义二叉树的节点
public class BstNode<T extends Comparable<T>>{
    // 定义数据域
    private T data;
    // 定义父节点
    private BstNode<T> parent;
    // 定义左节点
    private BstNode<T> left;
    // 定义右节点
    private BstNode<T> right;

    public BstNode() {
    }

    public BstNode(T data, BstNode<T> parent, BstNode<T> left, BstNode<T> right) {
        this.data = data;
        this.parent = parent;
        this.left = left;
        this.right = right;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public BstNode<T> getParent() {
        return parent;
    }

    public void setParent(BstNode<T> parent) {
        this.parent = parent;
    }

    public BstNode<T> getLeft() {
        return left;
    }

    public void setLeft(BstNode<T> left) {
        this.left = left;
    }

    public BstNode<T> getRight() {
        return right;
    }

    public void setRight(BstNode<T> right) {
        this.right = right;
    }
}

二叉搜索树的构建

二叉树的构建可以一共实例了两种方法,一种是递归的方法,在需要循环调用提供的接口,每次传入一个参数,另外一种是递归的方法,只需要一次性传入一个数组集合。
注意: 二叉搜索树由于实现了泛型操作,所以递归方法传入的数组必须是基本数据类型对应的包装类型的数组。

非递归方法

 // 插入节点,非递归方法
    public void insert(T data){
        // 新建节点
        BstNode<T> bstNode = new BstNode<T>(data, null, null, null);
        // 判断是否创建节点失败。
        if(bstNode != null){
        	// 创建成功则插入数据
            insert(this, bstNode);
        }
    }

递归方法

 	// 插入元素,递归方法
    public void insert(T[] data, int index){
    	// 递归结束标志符
        if(index == data.length){
            return;
        }
        // 创建节点值域
        BstNode<T> bstNode = new BstNode<>(data[index], null, null, null);
        if(bstNode != null){
            insert(this, bstNode);
        }
        insert(data, ++index);
    }

元素插入代码

// 插入节点 : 8,3,10,1,6,4,7,14,13
    private void insert(BstTreeTest<T> treeRoot, BstNode<T> node){
        int temp = 0;
        // 保存要插入的位置
        BstNode<T> bst = null;
        // 遍历树的节点
        BstNode<T> bstRoot = treeRoot.root;
        while (bstRoot != null){
            // 保存上一个根节点
            bst = bstRoot;
            // 将要插入的节点与根节点的数据进行比较
            temp = node.getData().compareTo(bstRoot.getData());
            // 如果插入的数据大于根节点的数据
            if(temp > 0){
                bstRoot = bstRoot.getRight();
            }else if(temp < 0){
              // 如果插入的数据小于根节点的数据。
              bstRoot = bstRoot.getLeft();
            }
        }
        // 插入的数据的父节点与保存的上一个根节点连接起来。
        node.setParent(bst);
        // 如果保存的根节点为空,则证明目前的二叉搜索树为空树
        if(bst == null){
            // 插入的节点直接当做树的根节点。
            treeRoot.setRoot(node);
        }else {
            // 插入数据的节点的父节点已经找到,目前还无法确定是插入到父节点的右边还是左边。
            temp = node.getData().compareTo(bst.getData());
            if(temp > 0){
                // 插入数据比父节点的数据大
                bst.setRight(node);
            }else {
                // 插入的数据比父节点的数据小。
                bst.setLeft(node);
            }
        }
    }

首先插入的是数字8,这个时候的二叉搜索树是空树,所以第一个插入的数字8便当做是树的根节点了,而parent,left,right这三个节点为null值。
在这里插入图片描述

第二步插入数字3,首先根节点已经确定是数字8了,这个时候可以进入while循环,调用compareTo()方法比较根节点的数字和数字3哪一个大,然后判断数字3应该插入根节点的左边还是右边。
在这里插入图片描述
第三步与第二步类似,如下图所示:
在这里插入图片描述
第四步插入数字1,数字1比根节点的数字8小,遍历根节点的左子树,数字1比数字3小,数字3没有子树,是一个叶子,所以数字1插入数字3的左边。
在这里插入图片描述
第五步插入数字6,插入的步骤调用网站工具生成:
在这里插入图片描述
第六步插入数字4,具体步骤如下:
在这里插入图片描述
第七步插入数字7,具体步骤如下:
在这里插入图片描述
第八步插入数字14,具体步骤如下:
在这里插入图片描述
第九步插入数字13,具体步骤如下:
在这里插入图片描述

遍历

二叉搜索树的遍历一共有三种,先序遍历,中序遍历和后序遍历。其中对于二叉搜索树而言,中序遍历可以实现二叉树值从小到大顺序访问排列。

先序遍历

若二叉树非空,则执行以下操作:

  • 访问根结点;
  • 先序遍历左子树;
  • 先序遍历右子树。
// 先序遍历外部接口: 8,3,10,1,6,4,7,14,13
    public void PreorderPrint(BstTreeTest<T> tree){
        if(tree != null){
            PreorderPrint(tree.getRoot());
        }
    }
    // 先序遍历内部接口
    private void PreorderPrint(BstNode<T> bstRoot){
        if(bstRoot != null){
            System.out.print(bstRoot.getData() + " ");
            // 先访问左子树
            PreorderPrint(bstRoot.getLeft());
            // 然后再访问右子树
            PreorderPrint(bstRoot.getRight());
        }
    }

中序遍历

若二叉树非空,则执行以下操作:

  • 中序遍历左子树;
  • 访问根结点;
  • 中序遍历右子树。
 // 中序遍历外部接口
    public void MiddlePrint(BstTreeTest<T> tree){
        if(tree != null){
            MiddlePrint(tree.getRoot());
        }
    }
    // 中序遍历内部接口
    private void MiddlePrint(BstNode<T> bstRoot){
        if(bstRoot != null){
            MiddlePrint(bstRoot.getLeft());
            System.out.print(bstRoot.getData() + " ");
            MiddlePrint(bstRoot.getRight());
        }
    }

后序遍历

若二叉树非空,则执行以下操作:

  • 后序遍历左子树;
  • 后序遍历右子树。
  • 访问根结点;
// 后序遍历外部接口
    public void LastPrint(BstTreeTest<T> tree){
        if(tree != null){
            LastPrint(tree.getRoot());
        }
    }
    // 后序遍历内部接口
    private void LastPrint(BstNode<T> bstRoot){
        if(bstRoot != null){
            LastPrint(bstRoot.getLeft());
            LastPrint(bstRoot.getRight());
            System.out.print(bstRoot.getData() + " ");
        }
    }

二叉搜索树构造:
在这里插入图片描述

结果:
在这里插入图片描述

遍历树的高度

递归遍历树的高度

// 递归遍历树的高度
    public int RecursionHeight(BstTreeTest<T> tree){
        return RecursionHeight(tree.getRoot());
    }
    private int RecursionHeight(BstNode<T> bstRoot){
        if(bstRoot == null){
            return 0;
        }
        // 递归寻找最大值,还要算上根节点,所以需要加1
        return 1 + Math.max(RecursionHeight(bstRoot.getLeft()), RecursionHeight(bstRoot.getRight()));
    }

非递归实现遍历树的高度

 // 非递归实现遍历树的高度
    public int NoRecursionHeight(BstTreeTest<T> tree){
        return NoRecursionHeight(tree.getRoot());
    }
    private int NoRecursionHeight(BstNode<T> bstRoot){
        if(bstRoot == null) {
            return 0;
        }
        // 树的高度
        int height = 0;
        // 每一层的树的数目
        int levelSize = 1;
        Queue<BstNode> queue = new LinkedList<>();
        // 根节点入队
        queue.offer(bstRoot);
        while (!queue.isEmpty()){
            // 出队
            BstNode<T> node = queue.poll();
            // 存储树的数目减1
            levelSize --;
            if (node.getLeft() != null){
                // 遍历左边
                queue.offer(node.getLeft());
            }
            if(node.getRight() != null){
                // 遍历右边
                queue.offer(node.getRight());
            }
            // 说明需要访问下一层了
            if(levelSize == 0){
                // 获取一层存储到的元素的个数
                levelSize = queue.size();
                // 高度加一
                height ++;
            }
        }
        return height;
    }

查找

递归查找

 // 递归版本查找
    public T findValue(T value){
        return findValue(this.getRoot(), value);
    }

    private T findValue(BstNode<T> bstRoot, T key){
    	// 如果查找的树为空树,直接返回null
        if(bstRoot == null){
            return null;
        }
        // 如果树不为空,则查找的元素跟根节点比较。
        int temp = bstRoot.getData().compareTo(key);
        if(temp > 0){
        	// 递归查找左子树
            return findValue(bstRoot.getLeft(), key);
        }else if(temp < 0){
        	// 递归查找右子树
            return findValue(bstRoot.getRight(), key);
        }
        return bstRoot.getData();
    }

非递归查找

 // 非递归版本查找
    public T Search(T key){
        return Search(this.getRoot(), key);
    }

    private T Search(BstNode<T> bstRoot, T key){
    	// 判断根节点是否为空,不为空,循环查找左子树或者是右子树
        while (bstRoot != null){
            int temp = bstRoot.getData().compareTo(key);
            if(temp > 0){
                bstRoot = bstRoot.getLeft();
            }else if(temp < 0){
                bstRoot = bstRoot.getRight();
            }else {
                return bstRoot.getData();
            }
        }
        return null;
    }

极大值

二叉搜索树有一个特点:构成二叉搜索树的数值最大值只能在右子树,所以查找最大值,只需要遍历查找右子树即可。

// 查找极大值
    public T findMax(BstTreeTest<T> tree){
        BstNode<T> value = findMax(tree.getRoot());
        if(tree == null || value == null){
            return  null;
        }
        return value.getData();
    }
    private BstNode<T> findMax(BstNode<T> bstRoot){
        if(bstRoot == null){
            return null;
        }
        while (bstRoot.getRight() != null){
            bstRoot = bstRoot.getRight();
        }
        return bstRoot;
    }

极小值

二叉搜索树的极小值同理。

// 查找极小值
    public T findMin(BstTreeTest<T> tree){
        BstNode<T> value = findMin(tree.getRoot());
        if(tree == null || value == null){
            return  null;
        }
        return value.getData();
    }
    private BstNode<T> findMin(BstNode<T> bstRoot){
        if(bstRoot == null){
            return null;
        }
        while (bstRoot.getLeft() != null){
            bstRoot = bstRoot.getLeft();
        }
        return bstRoot;
    }

前驱节点

前驱节点:二叉搜索树中小于当前节点的最大值
在这里插入图片描述
实例代码:

 // bstRoot节点是要查找的节点,查找前驱节点,也就是说查找二叉搜索树中数据值小于该查找节点的“最大节点值”
    public BstNode<T> findPrecursor(BstNode<T> bstRoot){
        // bstRoot节点中存在有左节点,那bstRoot节点的前驱节点是以其左孩子为根的子树的最大节点。
        if(bstRoot.getLeft() != null){
            return findMax(bstRoot.getLeft());
        }
        BstNode<T> node = bstRoot.getParent();
        // 如果bstRoot节点没有左孩子。则bstRoot节点有以下两种可能:
        // (01) bstRoot节点是"一个右孩子",则"bstRoot节点的前驱结点"为 "它的父结点"。
        // (01) bstRoot节点是"一个左孩子",则查找"bstRoot节点的最低的父结点,并且该父结点要具有右孩子",找到的这个"最低的父结点"就是"bstRoot节点的前驱结点"。
        while ((node != null) && (bstRoot == node.getLeft())){
            bstRoot = node;
            node = node.getParent();
        }
        // 第二种方法
//        BstNode<T> node = bstRoot.left;
//        while (node.right != null){
//            node = node.right;
//        }
        return node;
    }

后继节点

后继节点:二叉搜索树的数据中大于当前节点的最小值。
在这里插入图片描述
实例代码:

 // 查找后继节点,查找二叉搜索树中数据值大于该节点的最小节点
    public BstNode<T> findLastNode(BstNode<T> bstRoot){
        // bstRoot节点中存在有左节点,那bstRoot节点的前驱节点是以其左孩子为根的子树的最大节点。
        if(bstRoot.getRight() != null){
            return findMin(bstRoot.getRight());
        }
        BstNode<T> node = bstRoot.getParent();
        // 如果bstRoot节点没有右孩子。则bstRoot节点有以下两种可能:
        // (01) bstRoot节点是"一个左孩子",则"bstRoot节点的后继结点"为 "它的父结点"。
        // (01) bstRoot节点是"一个右孩子",则查找"bstRoot节点的最低的父结点,并且该父结点要具有左孩子",找到的这个"最低的父结点"就是"bstRoot节点的后继结点"。
        while ((node != null) && (bstRoot == node.getRight())){
            bstRoot = node;
            node = node.getParent();
        }
        // 第二种方法
//        BstNode<T> node = bstRoot.right;
//        while (node.left != null){
//            node = node.left;
//        }
        return node;
    }

删除

// 删除节点
    public BstNode<T> delete(T key){
        BstNode<T> delNode = null;
        delNode = deleteNode(this.getRoot(), key);
        return delNode;
    }
    private BstNode<T> deleteNode(BstNode<T> bstRoot, T key){
        if(bstRoot == null){
            return null;
        }
        int temp = bstRoot.getData().compareTo(key);
        if(temp > 0) {
            bstRoot.setLeft(deleteNode(bstRoot.getLeft(), key));
        }else if(temp < 0){
            bstRoot.setRight(deleteNode(bstRoot.getRight(), key));
        }else {
            if(bstRoot.getLeft() == null && bstRoot.getRight() == null){
                bstRoot = null;
            }else if(bstRoot.getLeft() != null){
                // 存在有左子树,寻找前驱节点
                bstRoot.setData(findPrecursor(bstRoot).getData());
                bstRoot.setLeft(deleteNode(bstRoot.getLeft(), bstRoot.getData()));
            }else {
                // 存在有右子树,寻找后继节点
                bstRoot.setData(findLastNode(bstRoot).getData());
                bstRoot.setRight(deleteNode(bstRoot.getRight(), bstRoot.getData()));
            }
        }
        return bstRoot;
    }

第一种情况: 删除节点8,因为根节点8有左右子树,所以根节点8的前驱节点7替换根节点8。
在这里插入图片描述
第二种情况: 被删除的节点只有左子树(右子树),那就由左子树(右子树)替换。
在这里插入图片描述

第三种情况: 删除的节点是叶子,直接删除。
在这里插入图片描述

销毁

// 销毁二叉搜索树
    public void clear(){
        destroy(this.getRoot());
        root = null;
    }
    private void destroy(BstNode<T> bstRoot){
        if(bstRoot == null){
            return;
        }
        if(bstRoot.getLeft() != null){
            destroy(bstRoot.getLeft());
        }
        if(bstRoot.getRight() != null){
            destroy(bstRoot.getRight());
        }
        bstRoot = null;
    }

全部代码

BSTNode类代码

/**
 * @author: 随风飘的云
 * @describe:
 * @date 2022/04/11 1:41
 */
// 定义二叉树的节点
public class BstNode<T extends Comparable<T>>{
    // 定义数据域
    private T data;
    // 定义父节点
    private BstNode<T> parent;
    // 定义左节点
    private BstNode<T> left;
    // 定义右节点
    private BstNode<T> right;

    public BstNode() {
    }

    public BstNode(T data, BstNode<T> parent, BstNode<T> left, BstNode<T> right) {
        this.data = data;
        this.parent = parent;
        this.left = left;
        this.right = right;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public BstNode<T> getParent() {
        return parent;
    }

    public void setParent(BstNode<T> parent) {
        this.parent = parent;
    }

    public BstNode<T> getLeft() {
        return left;
    }

    public void setLeft(BstNode<T> left) {
        this.left = left;
    }

    public BstNode<T> getRight() {
        return right;
    }

    public void setRight(BstNode<T> right) {
        this.right = right;
    }
}

BstTreeTest类代码

import java.util.LinkedList;
import java.util.Queue;

/**
 * @author: 随风飘的云
 * @describe:
 * @date 2022/04/09 1:24
 */


public class BstTreeTest<T extends Comparable<T>>{

    // 定义根节点
    private BstNode<T> root;
    // 定义无参数构造方法
    public BstTreeTest() {

    }

    public BstTreeTest(BstNode<T> root) {
        this.root = root;
    }

    // 获取二叉树的根节点
    public BstNode<T> getRoot() {
        return root;
    }

    // 设置二叉树的根节点
    public void setRoot(BstNode<T> root) {
        this.root = root;
    }

    //------------------------------------ 增加 --------------------------------------/

    // 插入节点,非递归方法
    public void insert(T data){
        // 新建节点
        BstNode<T> bstNode = new BstNode<T>(data, null, null, null);
        // 判断是否创建节点失败。
        if(bstNode != null){
            insert(this, bstNode);
        }
    }

    // 插入元素,递归方法
    public void insert(T[] data, int index){
        if(index == data.length){
            return;
        }
        // 创建节点值域
        BstNode<T> bstNode = new BstNode<>(data[index], null, null, null);
        if(bstNode != null){
            insert(this, bstNode);
        }
        insert(data, ++index);
    }

    // 插入节点
    private void insert(BstTreeTest<T> treeRoot, BstNode<T> node){
        int temp = 0;
        BstNode<T> bst = null;
        BstNode<T> bstRoot = treeRoot.root;

        while (bstRoot != null){
            // 保存上一个根节点
            bst = bstRoot;
            // 将要插入的节点与根节点的数据进行比较
            temp = node.getData().compareTo(bstRoot.getData());
            // 如果插入的数据大于根节点的数据
            if(temp > 0){
                bstRoot = bstRoot.getRight();
            }else if(temp < 0){
              // 如果插入的数据小于根节点的数据。
              bstRoot = bstRoot.getLeft();
            }
        }
        // 插入的数据的父节点与保存的上一个根节点连接起来。
        node.setParent(bst);
        // 如果保存的根节点为空,则证明目前的二叉搜索树为空树
        if(bst == null){
            // 插入的节点直接当做树的根节点。
            treeRoot.setRoot(node);
        }else {
            // 插入数据的节点的父节点已经找到,目前还无法确定是插入到父节点的右边还是左边。
            temp = node.getData().compareTo(bst.getData());
            if(temp > 0){
                // 插入数据比父节点的数据大
                bst.setRight(node);
            }else {
                // 插入的数据比父节点的数据小。
                bst.setLeft(node);
            }
        }
    }

    //------------------------------------ 遍历 --------------------------------------//

    // 先序遍历外部接口
    public void PreorderPrint(BstTreeTest<T> tree){
        if(tree != null){
            PreorderPrint(tree.getRoot());
        }
    }
    // 先序遍历内部接口
    private void PreorderPrint(BstNode<T> bstRoot){
        if(bstRoot != null){
            System.out.print(bstRoot.getData() + " ");
            PreorderPrint(bstRoot.getLeft());
            PreorderPrint(bstRoot.getRight());
        }
    }

    // 中序遍历外部接口
    public void MiddlePrint(BstTreeTest<T> tree){
        if(tree != null){
            MiddlePrint(tree.getRoot());
        }
    }
    // 中序遍历内部接口
    private void MiddlePrint(BstNode<T> bstRoot){
        if(bstRoot != null){
            MiddlePrint(bstRoot.getLeft());
            System.out.print(bstRoot.getData() + " ");
            MiddlePrint(bstRoot.getRight());
        }
    }

    // 后序遍历外部接口
    public void LastPrint(BstTreeTest<T> tree){
        if(tree != null){
            LastPrint(tree.getRoot());
        }
    }
    // 后序遍历内部接口
    private void LastPrint(BstNode<T> bstRoot){
        if(bstRoot != null){
            LastPrint(bstRoot.getLeft());
            LastPrint(bstRoot.getRight());
            System.out.print(bstRoot.getData() + " ");
        }
    }

    // 递归遍历树的高度
    public int RecursionHeight(BstTreeTest<T> tree){
        return RecursionHeight(tree.getRoot());
    }
    private int RecursionHeight(BstNode<T> bstRoot){
        if(bstRoot == null){
            return 0;
        }
        // 递归寻找最大值,还要算上根节点,所以需要加1
        return 1 + Math.max(RecursionHeight(bstRoot.getLeft()), RecursionHeight(bstRoot.getRight()));
    }

    // 非递归实现遍历树的高度
    public int NoRecursionHeight(BstTreeTest<T> tree){
        return NoRecursionHeight(tree.getRoot());
    }
    private int NoRecursionHeight(BstNode<T> bstRoot){
        if(bstRoot == null) {
            return 0;
        }
        // 树的高度
        int height = 0;
        // 每一层的树的数目
        int levelSize = 1;
        Queue<BstNode> queue = new LinkedList<>();
        // 根节点入队
        queue.offer(bstRoot);
        while (!queue.isEmpty()){
            // 出队
            BstNode<T> node = queue.poll();
            levelSize --;
            if (node.getLeft() != null){
                // 遍历左边
                queue.offer(node.getLeft());
            }
            if(node.getRight() != null){
                // 遍历右边
                queue.offer(node.getRight());
            }
            // 说明需要访问下一层了
            if(levelSize == 0){
                // 获取一层存储到的元素的个数
                levelSize = queue.size();
                // 高度加一
                height ++;
            }
        }
        return height;
    }


    //------------------------------------ 极值  --------------------------------------//

    // 查找极大值
    public T findMax(BstTreeTest<T> tree){
        BstNode<T> value = findMax(tree.getRoot());
        if(tree == null || value == null){
            return  null;
        }
        return value.getData();
    }
    private BstNode<T> findMax(BstNode<T> bstRoot){
        if(bstRoot == null){
            return null;
        }
        while (bstRoot.getRight() != null){
            bstRoot = bstRoot.getRight();
        }
        return bstRoot;
    }

    // 查找极小值
    public T findMin(BstTreeTest<T> tree){
        BstNode<T> value = findMin(tree.getRoot());
        if(tree == null || value == null){
            return  null;
        }
        return value.getData();
    }
    private BstNode<T> findMin(BstNode<T> bstRoot){
        if(bstRoot == null){
            return null;
        }
        while (bstRoot.getLeft() != null){
            bstRoot = bstRoot.getLeft();
        }
        return bstRoot;
    }

    //------------------------------------ 前驱和后继 --------------------------------------//

    // bstRoot节点是要查找的节点,查找前驱节点,也就是说查找二叉搜索树中数据值小于该查找节点的“最大节点值”
    public BstNode<T> findPrecursor(BstNode<T> bstRoot){
        // bstRoot节点中存在有左节点,那bstRoot节点的前驱节点是以其左孩子为根的子树的最大节点。
        if(bstRoot.getLeft() != null){
            return findMax(bstRoot.getLeft());
        }
        BstNode<T> node = bstRoot.getParent();
        // 如果bstRoot节点没有左孩子。则bstRoot节点有以下两种可能:
        // (01) bstRoot节点是"一个右孩子",则"bstRoot节点的前驱结点"为 "它的父结点"。
        // (01) bstRoot节点是"一个左孩子",则查找"bstRoot节点的最低的父结点,并且该父结点要具有右孩子",找到的这个"最低的父结点"就是"bstRoot节点的前驱结点"。
        while ((node != null) && (bstRoot == node.getLeft())){
            bstRoot = node;
            node = node.getParent();
        }
        // 第二种方法
//        BstNode<T> node = bstRoot.left;
//        while (node.right != null){
//            node = node.right;
//        }
        return node;
    }

    // 查找后继节点,查找二叉搜索树中数据值大于该节点的最小节点
    public BstNode<T> findLastNode(BstNode<T> bstRoot){
        // bstRoot节点中存在有左节点,那bstRoot节点的前驱节点是以其左孩子为根的子树的最大节点。
        if(bstRoot.getRight() != null){
            return findMin(bstRoot.getRight());
        }
        BstNode<T> node = bstRoot.getParent();
        // 如果bstRoot节点没有右孩子。则bstRoot节点有以下两种可能:
        // (01) bstRoot节点是"一个左孩子",则"bstRoot节点的后继结点"为 "它的父结点"。
        // (01) bstRoot节点是"一个右孩子",则查找"bstRoot节点的最低的父结点,并且该父结点要具有左孩子",找到的这个"最低的父结点"就是"bstRoot节点的后继结点"。
        while ((node != null) && (bstRoot == node.getRight())){
            bstRoot = node;
            node = node.getParent();
        }
        // 第二种方法
//        BstNode<T> node = bstRoot.right;
//        while (node.left != null){
//            node = node.left;
//        }
        return node;
    }

    //------------------------------------ 查找 --------------------------------------//

    // 递归版本查找
    public T findValue(T value){
        return findValue(this.getRoot(), value);
    }

    private T findValue(BstNode<T> bstRoot, T key){
        if(bstRoot == null){
            return null;
        }
        int temp = bstRoot.getData().compareTo(key);
        if(temp > 0){
            return findValue(bstRoot.getLeft(), key);
        }else if(temp < 0){
            return findValue(bstRoot.getRight(), key);
        }
        return bstRoot.getData();
    }

    // 非递归版本查找
    public T Search(T key){
        return Search(this.getRoot(), key);
    }

    private T Search(BstNode<T> bstRoot, T key){
        while (bstRoot != null){
            int temp = bstRoot.getData().compareTo(key);
            if(temp > 0){
                bstRoot = bstRoot.getLeft();
            }else if(temp < 0){
                bstRoot = bstRoot.getRight();
            }else {
                return bstRoot.getData();
            }
        }
        return null;
    }

    //------------------------------------ 删除 --------------------------------------//
    // 删除节点
    public BstNode<T> delete(T key){
        BstNode<T> delNode = null;
        delNode = deleteNode(this.getRoot(), key);
        return delNode;
    }
    private BstNode<T> deleteNode(BstNode<T> bstRoot, T key){
        if(bstRoot == null){
            return null;
        }
        int temp = bstRoot.getData().compareTo(key);
        if(temp > 0) {
            bstRoot.setLeft(deleteNode(bstRoot.getLeft(), key));
        }else if(temp < 0){
            bstRoot.setRight(deleteNode(bstRoot.getRight(), key));
        }else {
            if(bstRoot.getLeft() == null && bstRoot.getRight() == null){
                bstRoot = null;
            }else if(bstRoot.getLeft() != null){
                // 存在有左子树,寻找前驱节点
                bstRoot.setData(findPrecursor(bstRoot).getData());
                bstRoot.setLeft(deleteNode(bstRoot.getLeft(), bstRoot.getData()));
            }else {
                // 存在有右子树,寻找后继节点
                bstRoot.setData(findLastNode(bstRoot).getData());
                bstRoot.setRight(deleteNode(bstRoot.getRight(), bstRoot.getData()));
            }
        }
        return bstRoot;
    }
    //------------------------------------ 销毁 --------------------------------------//

    // 销毁二叉搜索树
    public void clear(){
        destroy(this.getRoot());
        root = null;
    }
    private void destroy(BstNode<T> bstRoot){
        if(bstRoot == null){
            return;
        }
        if(bstRoot.getLeft() != null){
            destroy(bstRoot.getLeft());
        }
        if(bstRoot.getRight() != null){
            destroy(bstRoot.getRight());
        }
        bstRoot = null;
    }
 }

测试代码

public static void main(String[] args) {
        Integer[] str = new Integer[]{8,3,10,1,6,4,7,14,13};

        BstTreeTest<Integer> test = new BstTreeTest<>();
        int length = str.length;

        for (int i = 0; i < length; i++) {
            test.insert(str[i]);
        }

        System.out.print("先序遍历的结果为:");
        test.PreorderPrint(test);
        System.out.println();

        System.out.print("中序遍历的结果为:");
        test.MiddlePrint(test);
        System.out.println();

        System.out.print("后序遍历的结果为:");
        test.LastPrint(test);
        System.out.println();

        System.out.println("查找极大值的结果为:" + test.findMax(test));
        System.out.println("查找极小值的结果为:" + test.findMin(test));

        System.out.println("树的高度递归遍历结果为:" + test.NoRecursionHeight(test));
        System.out.println("树的高度非递归遍历结果为:" + test.RecursionHeight(test));

        System.out.println("值的递归遍历结果为:" + test.findValue(14));
        System.out.println("值的非递归遍历结果为:" + test.Search(13));

        System.out.println("特定节点的前驱节点为:" + test.findPrecursor(test.getRoot()).getData());
        System.out.println("特定节点的后继节点为:" + test.findLastNode(test.getRoot()).getData());

        test.root = test.delete(8);

        System.out.print("删除节点后的先序遍历的结果为:");
        test.PreorderPrint(test);
        System.out.println();

        System.out.print("删除节点后的中序遍历的结果为:");
        test.MiddlePrint(test);
        System.out.println();

        System.out.print("删除节点后的后序遍历的结果为:");
        test.LastPrint(test);
        System.out.println();
    }

结果

在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值