BST树

一、BST树

二叉查找树(Binary Search Tree),又名二叉搜索树或二叉排序树。可以是一颗空树,或者是具有下列性质的二叉树:

(1)若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;

(2)若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值。
在这里插入图片描述
定义BST树:

/**
 * BST树节点的实现
 * @param <T>
 */
class BSTNode <T extends Comparable<T>> {
    private T data;
    private BSTNode<T> left;
    private BSTNode<T> right;

    public BSTNode(T data){
        this.data = data;
    }

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

    public T getData() {
        return data;
    }

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

    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;
    }
}
/**
 * BST 树的实现
 * @param <T>
 */
class BST<T extends Comparable<T>>{
    private BSTNode<T> root; //指向BST树的根节点
    public BST(){
        this.root = null;
    }
}

二、查找操作

BST树查找非常简单,从根节点开始查找,如果要查找的数比根节点小,就在左子树上继续查找,如果大于根节点的数,就在右子树上继续查找。整个查找过程就是从根节点开始一直向下的一条路径,若假设树的高度是n,那么查找过程的时间复杂度就是O(n)。

递归实现
public T query(T val){
    return query(this.root,val);
}

private T query(BSTNode<T> root, T val) {
    if (this.root == null){  //空树
        return null;
    }
    if (root.getData().compareTo(val) > 0){ //val小于根节点的数
        return query(root.getLeft(),val); //左子树查找
    }else if (root.getData().compareTo(val) < 0){ //val大于根节点的数
        return query(root.getRight(),val); //右子树查找
    }else { //val等于根节点的数
        return root.getData(); //返回该值
    }
}
非递归实现
public T nonrecur_query(T val){
    if (this.root == null){
        return null;
    }
    //如果根节点不为空,则从根节点开始搜索
    BSTNode<T> parent = null;
    BSTNode<T> cur = this.root;
    while (cur != null){
        parent = cur;
        if (cur.getData().compareTo(val) > 0){ //查找的数据小于cur
            cur = cur.getLeft(); //左子树查找
        }else if (cur.getData().compareTo(val) < 0){ //查找的数据大于cur
            cur = cur.getRight(); //右子树查找
        }else {
            return cur.getData();
        }
    }
    return null;
}

三、插入操作

插入操作同查询类似,如果要插入的数比根节点小,就在左子树上搜索可以插入的位置,反之在右子树上查找。
如果在上述BST树要插入7,如下图示:
在这里插入图片描述

递归实现
/**
 * BST 树递归插入函数接口
 * @param val
 */
public void insert(T val){
    this.root = insert(this.root,val);
}

/**
 * BST树递归插入的具体实现函数
 * @param root
 * @param val
 * @return
 */
private BSTNode<T> insert(BSTNode<T> root, T val) {
    if (root == null){ //找到插入的位置
        return new BSTNode<>(val); //进行插入
    }
    if (root.getData().compareTo(val) > 0){ //val < 根节点的值
        root.setLeft(insert(root.getLeft(),val)); //递归左子树
    }
    if (root.getData().compareTo(val) < 0){ //val > 根节点的值
        root.setRight(insert(root.getRight(),val)); //递归右子树
    }
    return root; //将当前节点返回到父节点的函数调用中
}

非递归实现
public void noncur_insert(T val){
    //如果根节点为空,直接插入为根节点
    if (this.root == null){
        this.root = new BSTNode<T>(val);
        return;
    }
    //如果根节点不为空,则从根节点开始搜索
    BSTNode<T> parent = null;
    BSTNode<T> cur = this.root;
    while (cur != null){
        parent = cur;
        if (cur.getData().compareTo(val) > 0){ //插入的数据小于cur
            cur = cur.getLeft(); //左子树查找
        }else if (cur.getData().compareTo(val) < 0){ //插入的数据大于cur
            cur = cur.getRight(); //右子树查找
        }else {
            return;
        }
    }
    //查到最后,大的话就给右子树加,小的话就给左子树加
    if (parent.getData().compareTo(val) > 0){
        parent.setLeft(new BSTNode<T>(val));
    }else {
        parent.setRight(new BSTNode<T>(val));
    }
}

四、删除操作

在了解删除操作之前,先来介绍一下前驱节点和后继节点

  • 前驱节点: 待删除节点左子树中值最大的节点
  • 后继节点: 待删除节点右子树中值最小的节点

删除操作分为三种情况

  1. 待删除的节点没有孩子节点,把删除节点的父节点的相应地址域置为null即可
    在这里插入图片描述
  2. 待删除的节点只有一个孩子节点,把删除节点的子树根节点写入父节点相应的地址域
    在这里插入图片描述
  3. 待删除节点有两个孩子节点,找到删除节点的前驱结点或者后继节点,用前驱或者后继把当前删除节点的值覆盖掉,然后删除前驱或者后继
    在这里插入图片描述
递归实现
    /**
     * 递归删除val元素的函数接口
     * @param val
     */
    public void remove(T val){
        this.root = remove(root,val);
    }

    /**
     * 递归实现BST删除函数
     * @param root
     * @param val
     * @return
     */
    public BSTNode<T> remove(BSTNode<T> root,T val){
        if (root == null){
            return null;
        }

        if (root.getData().compareTo(val) > 0){
            root.setLeft(remove(root.getLeft(),val));
        }else if (root.getData().compareTo(val) < 0){
            root.setRight(remove(root.getRight(),val));
        }else { //找到要删除的节点了
            if (root.getLeft() != null && root.getRight()!= null){ //待删除的节点有两个孩子节点
                BSTNode<T> pre = root.getLeft();
                while (pre.getRight() != null){
                    pre = pre.getRight();
                }
                root.setData(pre.getData()); //用前驱覆盖掉root的值
                root.setLeft(remove(root.getLeft(),pre.getData())); //删除前驱节点
            }else if (root.getLeft() != null){ //只有一个节点,直接将孩子节点返回给父节点的函数调用
                return root.getLeft();
            }else if (root.getRight() != null){
                return root.getRight();
            }else {
                return null;
            }
        }
        return root;
    }

非递归实现
    /**
     * BST树的非递归删除(前驱节点实现)
     * @param val
     */
    public void noncur_remove(T val){
        BSTNode<T> parent= null; //待删除节点的父节点
        BSTNode<T> cur = this.root;

        //搜索BST树,找到待删除的节点
        while (cur != null){
            if (cur.getData().compareTo(val) > 0){ //待删除的节点的值小于当前节点的值
                parent = cur;
                cur = cur.getLeft(); //在左子树找
            }else if (cur.getData().compareTo(val) < 0){ //待删除的节点的值大于当前节点的值
                parent = cur;
                cur = cur.getRight(); //在右子树找
            }else { //cur就是要删除的节点
                break; //跳出循环
            }
        }

        /**
         * 循环结束的两种情况:
         * 1.找到cur即待删除的节点
         * 2.cur=null,BST树里没有该节点
         */
        if (cur == null){
            System.out.println("该BST树中不存在值为"+val+"的节点,删除失败!");
            return;
        }

        //先把情况1处理成后两种情况
        //有两个孩子的节点
        if (cur.getLeft() != null && cur.getRight() != null){
            //用前驱节点代替删除节点,然后删除前驱节点
            BSTNode<T> old = cur; //old记录要删除的节点
            parent = cur;
            cur = cur.getLeft(); //找前驱
            //找左子树中值最大的节点
            while (cur.getRight() != null){ //往右边找
                parent = cur;
                cur = cur.getRight();
            }
            //循环结束,cur指向前驱
            //用前驱节点的值代替待删除的节点old的值
            old.setData(cur.getData());
        }

        //处理情况 2 和 3
        //删除只有一个孩子的节点或者没有孩子的节点
        BSTNode<T> child = cur.getLeft();
        if (child == null){ //如果没有左孩子
            child = cur.getRight(); //指向右孩子节点
        } //此时child指向cur唯一的孩子节点

        if (parent == null){ //要删除的是根节点
            this.root = child;
        }else if (parent.getLeft() == cur){ //只有左子树有节点
            parent.setLeft(child);
        }else { //只有右子树有节点
            parent.setRight(child);
        }
    }

下一步就可以了解AVL树了,可以参考这个博客哦AVL树

好的,下面是我重新设计的C语言版本的BST模块: ``` // bst.h // 定义二叉搜索节点类型 typedef struct bst_node { int key; struct bst_node* left; struct bst_node* right; } BSTNode; // 创建一个新的节点 BSTNode* create_node(int key); // 插入一个节点,返回根节点 BSTNode* insert_node(BSTNode* root, int key); // 删除一个节点,返回根节点 BSTNode* delete_node(BSTNode* root, int key); // 查找一个节点,返回指向该节点的指针 BSTNode** search_node(BSTNode** root, int key); // 遍历二叉搜索,并将节点的key值存储到数组中 void traverse_bst(BSTNode* root, int* arr, int* index); // 释放二叉搜索的内存 void free_bst(BSTNode* root); ``` ```c // bst.c #include "bst.h" #include <stdlib.h> // 创建一个新的节点 BSTNode* create_node(int key) { BSTNode* node = (BSTNode*)malloc(sizeof(BSTNode)); node->key = key; node->left = NULL; node->right = NULL; return node; } // 插入一个节点,返回根节点 BSTNode* insert_node(BSTNode* root, int key) { if (root == NULL) { return create_node(key); } if (key < root->key) { root->left = insert_node(root->left, key); } else if (key > root->key) { root->right = insert_node(root->right, key); } return root; } // 删除一个节点,返回根节点 BSTNode* delete_node(BSTNode* root, int key) { if (root == NULL) { return NULL; } if (key < root->key) { root->left = delete_node(root->left, key); } else if (key > root->key) { root->right = delete_node(root->right, key); } else { if (root->left == NULL) { BSTNode* temp = root->right; free(root); return temp; } else if (root->right == NULL) { BSTNode* temp = root->left; free(root); return temp; } BSTNode* temp = root->right; while (temp->left != NULL) { temp = temp->left; } root->key = temp->key; root->right = delete_node(root->right, temp->key); } return root; } // 查找一个节点,返回指向该节点的指针 BSTNode** search_node(BSTNode** root, int key) { if (*root == NULL || (*root)->key == key) { return root; } if (key < (*root)->key) { return search_node(&((*root)->left), key); } else { return search_node(&((*root)->right), key); } } // 遍历二叉搜索,并将节点的key值存储到数组中 void traverse_bst(BSTNode* root, int* arr, int* index) { if (root == NULL) { return; } traverse_bst(root->left, arr, index); arr[*index] = root->key; (*index)++; traverse_bst(root->right, arr, index); } // 释放二叉搜索的内存 void free_bst(BSTNode* root) { if (root == NULL) { return; } free_bst(root->left); free_bst(root->right); free(root); } ``` 这个BST模块包含了创建节点、插入节点、删除节点、查找节点、遍历和释放内存等基本操作。在使用时,可以通过调用这些函数来实现对BST的操作。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值