BST二叉查找树(二叉排序树)java

1.介绍

1.1定义

二叉查找树(Binary Search Tree),是一种基础数据结构,支持多种动态集合的操作,search,insert,delete,minimum和maxmum

1.2二叉树的性质:
  • 要么是一个空树
  • 要么具备以下性质
    (1)若左子树非空,则左子树上的所有节点的关键字值均小于根节点的关键字值;
    (2)若右子树非空,则右子树上的所有节点的关键字值均大于根节点的关键字值;
    (3)左右子树也分别是一个二叉查找树
    在这里插入图片描述

2.二叉查找树的java实现

2.1 二叉查找树节点的定义

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

    private BSTNode<T> mRoot;    // 根结点

    public class BSTNode<T extends Comparable<T>> {
        T key;                // 关键字(键值)
        BSTNode<T> left;      // 左孩子
        BSTNode<T> right;     // 右孩子
        BSTNode<T> parent;    // 父结点

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

        ......
}

BSTNode包含二叉查找树的几个基本信息:
(01) key – 它是关键字,是用来对二叉查找树的节点进行排序的。
(02) left – 它指向当前节点的左孩子。
(03) right – 它指向当前节点的右孩子。
(04) parent – 它指向当前节点的父结点。

2.2二叉树的三种遍历

前序遍历,中序遍历,后序遍历

2.2.1 前序遍历

若二叉树非空,则执行以下操作:
(01) 访问根结点;
(02) 先序遍历左子树;
(03) 先序遍历右子树。

前序遍历代码

private void preOrder(BSTNode<T> tree) {
    if(tree != null) {
        System.out.print(tree.key+" ");
        preOrder(tree.left);
        preOrder(tree.right);
    }
}

public void preOrder() {
    preOrder(mRoot);
}
2.2.2 中序遍历

若二叉树非空,则执行以下操作:
(01) 中序遍历左子树;
(02) 访问根结点;
(03) 中序遍历右子树。

中序遍历代码:

private void inOrder(BSTNode<T> tree) {
    if(tree != null) {
        inOrder(tree.left);
        System.out.print(tree.key+" ");
        inOrder(tree.right);
    }
}

public void inOrder() {
    inOrder(mRoot);
}
2.2.3后序遍历

若二叉树非空,则执行以下操作:
(01) 后序遍历左子树;
(02) 后序遍历右子树;
(03) 访问根结点。

后序遍历代码:

private void postOrder(BSTNode<T> tree) {
    if(tree != null)
    {
        postOrder(tree.left);
        postOrder(tree.right);
        System.out.print(tree.key+" ");
    }
}

public void postOrder() {
    postOrder(mRoot);
}
3.查找
3.1递归版本
/*
 * (递归实现)查找"二叉树x"中键值为key的节点
 */
private BSTNode<T> search(BSTNode<T> x, T key) {
    if (x==null)
        return x;

    int cmp = key.compareTo(x.key);
    if (cmp < 0)
        return search(x.left, key);
    else if (cmp > 0)
        return search(x.right, key);
    else
        return x;
}

public BSTNode<T> search(T key) {
    return search(mRoot, key);
}
3.2非递归版本
/*
 * (非递归实现)查找"二叉树x"中键值为key的节点
 */
private BSTNode<T> iterativeSearch(BSTNode<T> x, T key) {
    while (x!=null) {
        int cmp = key.compareTo(x.key);

        if (cmp < 0) 
            x = x.left;
        else if (cmp > 0) 
            x = x.right;
        else
            return x;
    }

    return x;
}

public BSTNode<T> iterativeSearch(T key) {
    return iterativeSearch(mRoot, key);
}
4.Minimum和Maximum
4.1Minimum
/* 
 * 查找最小结点:返回tree为根结点的二叉树的最小结点。
 */
private BSTNode<T> minimum(BSTNode<T> tree) {
    if (tree == null)
        return null;

    while(tree.left != null)
        tree = tree.left;
    return tree;
}

public T minimum() {
    BSTNode<T> p = minimum(mRoot);
    if (p != null)
        return p.key;

    return null;
}
4.2 Maximum
/* 
 * 查找最大结点:返回tree为根结点的二叉树的最大结点。
 */
private BSTNode<T> maximum(BSTNode<T> tree) {
    if (tree == null)
        return null;

    while(tree.right != null)
        tree = tree.right;
    return tree;
}

public T maximum() {
    BSTNode<T> p = maximum(mRoot);
    if (p != null)
        return p.key;

    return null;
}
5.插入

思路: 先循环比较,找到插入的地方,然后再判断是根节点还是作左叶子还是又叶子

/* 
 * 将结点插入到二叉树中
 *
 * 参数说明:
 *     tree 二叉树的
 *     z 插入的结点
 */
private void insert(BSTree<T> bst, BSTNode<T> z) {
    int cmp;
    BSTNode<T> y = null;
    BSTNode<T> x = bst.mRoot;

    // 查找z的插入位置
    while (x != null) {
        y = x;
        cmp = z.key.compareTo(x.key);
        if (cmp < 0)
            x = x.left;
        else
            x = x.right;
    }

    z.parent = y;
    if (y==null)
        bst.mRoot = z;
    else {
        cmp = z.key.compareTo(y.key);
        if (cmp < 0)
            y.left = z;
        else
            y.right = z;
    }
}

/* 
 * 新建结点(key),并将其插入到二叉树中
 *
 * 参数说明:
 *     tree 二叉树的根结点
 *     key 插入结点的键值
 */
public void insert(T key) {
    BSTNode<T> z=new BSTNode<T>(key,null,null,null);

    // 如果新建结点失败,则返回。
    if (z != null)
        insert(this, z);
}
6.前驱后继

节点的前驱: 是该节点的左子树中的最大节点。
节点的后继: 是该节点的右子树中的最小节点。

6.1前驱
/* 
 * 找结点(x)的前驱结点。即,查找"二叉树中数据值小于该结点"的"最大结点"。
 */
public BSTNode<T> predecessor(BSTNode<T> x) {
    // 如果x存在左孩子,则"x的前驱结点"为 "以其左孩子为根的子树的最大结点"。
    if (x.left != null)
        return maximum(x.left);

    // 如果x没有左孩子。则x有以下两种可能:
    // (01) x是"一个右孩子",则"x的前驱结点"为 "它的父结点"。
    // (01) x是"一个左孩子",则查找"x的最低的父结点,并且该父结点要具有右孩子",找到的这个"最低的父结点"就是"x的前驱结点"。
    BSTNode<T> y = x.parent;
    while ((y!=null) && (x==y.left)) {
        x = y;
        y = y.parent;
    }

    return y;
}
6.2 后继
/* 
 * 找结点(x)的后继结点。即,查找"二叉树中数据值大于该结点"的"最小结点"。
 */
public BSTNode<T> successor(BSTNode<T> x) {
    // 如果x存在右孩子,则"x的后继结点"为 "以其右孩子为根的子树的最小结点"。
    if (x.right != null)
        return minimum(x.right);

    // 如果x没有右孩子。则x有以下两种可能:
    // (01) x是"一个左孩子",则"x的后继结点"为 "它的父结点"。
    // (02) x是"一个右孩子",则查找"x的最低的父结点,并且该父结点要具有左孩子",找到的这个"最低的父结点"就是"x的后继结点"。
    BSTNode<T> y = x.parent;
    while ((y!=null) && (x==y.right)) {
        x = y;
        y = y.parent;
    }

    return y;
}
7. 删除(星标!!!!!)

删除操作需要判断的情况是比较复杂的。 分为三种情况:

  • 没有子节点
    针对这种情况,直接删除即可。
//这里忽略了父节点不存在的情况,最后会巧妙的处理这种情况
if(node.parent.left == node){
    node.parent.left = null;
} else {
    node.parent.right = null;
}   
  • 只有左节点或只有右节点
    这种情况,左节点或者右节点来顶替这个节点
//先找到子节点,不需要管他是左是右
BSTNode<T> child = null;
if(node.left != null){
    child = node.left;
} else {
    child = node.right;
}
//这里忽略了父节点不存在的情况,最后会巧妙的处理这种情况
//将父节点和子节点建立关系
if(node.parent.left == node){
    node.parent.left = child;
} else {
    node.parent.right = child;
}
child.parent = node.parent;
  • 左右节点都有
    用后继节点来顶替这个节点
    在这里插入图片描述
    在这里插入图片描述
//获取当前节点的后继结点
Node<T> successor = successor(node);
//转移值
node.key = successor.key;
//后续变成删除 successor,就变成了前两种情况
//在图示例子中,就是第一种没有子节点的情况
node = successor;

总的代码:
将三种情况融合。先处理左右节点都有的情况。处理完之后,剩下的两种情况可以合并处理。

  1. 先看node有没有左右节点,有的话直接赋值给定义好的child节点。
  2. 然后再判断child是不是空的,不是的话,交接一下,node的父节点赋值给child的父节点。空的话,有妙用。
  3. 最后,判断node有没有父节点,没有的话,child节点直接就是根节点。有的话,则判断node是左节点还是有节点,交接一下。(此处的交接就看出2中child为空的妙用了,为空在3中的交接处理的是没有左右节点的这种情况,用空给抹去了node)
private BSTNode<T> delete(BSTNode<T> node) {
    //第 3 种情况,如果同时存在左右子节点
    if (node.left != null && node.right != null){
        //获取后继结点
        BSTNode<T> successor = successor(node);
        //转移后继结点值到当前节点
        node.key = successor.key;
        //把要删除的当前节点设置为后继结点
        node = successor;
    }
    //经过前一步处理,下面只有前两种情况,只能是一个节点或者没有节点
    //不管是否有子节点,都获取子节点
    BSTNode<T> child;
    if (node.left != null)
        child = node.left;
    else
        child = node.right;
    //如果 child != null,就说明是有一个节点的情况    
    if (child != null)
        //将子节点和父节点关联上
        child.parent = node.parent;
    //如果当前节点没有父节点(后继情况到这儿时一定有父节点)
    //说明要删除的就是根节点
    if (node.parent == null)
        //根节点设置为子节点
        //按照前面逻辑,根只有一个或者没有节点,所以直接赋值 child 即可
        mRoot = child;
    else if (node == node.parent.left)//存在父节点,并且当前节点是左节点时
        //将父节点的左节点设置为 child
        node.parent.left = child;
    else//右节点时
        //将父节点的右节点设置为 child
        node.parent.right = child;
    //返回被删除的节点
    return node;
}
//删除指定的值
public void delete(T key) {
    //获取要删除的节点
    BSTNode<T> node = search(mRoot, key);
    //如果存在就删除
    if (node != null)
       delete(node);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值