数据结构——手写一棵漂亮的二叉搜索树

数据结构——二叉搜索树

基本介绍

二叉搜索树也可以叫二叉排序树是查找算法中的一种数据结构的组织形式,其有特殊的规定,根据对关键字进行一系列的数据规划,其平均查找过程能达到O(log2(n))

对于二叉搜索树,其有下列的规定,一颗二叉搜索树,其必定是满足根结点的关键字大于其左子树的关键字,小于其右子树的关键字,等于可以左右摇摆,但是也需要进行规定,具体的式子表达就是
k e y l e f t < k e y r o o t < k e y r i g h t key_{left}<key_{root}<key_{right} keyleft<keyroot<keyright
一颗树如果是二叉搜索树,那么它需要满足下列条件

  • 如果左孩子不为空,那么其结点值小于其根结点值,如果右孩子不为空,那么其右孩子结点值大于其根结点值
  • 其左右子树本身又是一颗二叉排序树
    在这里插入图片描述

上面就是一颗二叉排序树的示例,因为二叉树的这种特性,所以其中序遍历一定是一个升序的数组

基本算法

一个二叉排序树包含许多基本操作、插入结点、查找结点、删除结点,这些操作都是需要保证是不影响数据结构本身是二叉排序树的性质的情况下而进行的操作

插入结点

往二叉排序树中插入结点其实很简单,其递归模型为
f ( k e y , r o o t ) = { f ( k e y , r o o t . l e f t ) − − k e y < r o o t . k e y f ( k e y , r o o t . r i g h t ) − − k e y > r o o t . k e y i n s e r t N o d e ( k e y ) − − r o o t = n u l l f(key,root) = \begin{cases}f(key,root.left)--key<root.key\\f(key,root.right)--key>root.key\\insertNode(key)--root=null\end{cases} f(key,root)=f(key,root.left)key<root.keyf(key,root.right)key>root.keyinsertNode(key)root=null
查找结点

查找结点也是在结合二叉排序树本身数据组织性质的情况下,可以很简单的实现,其递归模型为
s ( k e y , r o o t ) = { s ( k e y , r o o t . l e f t ) − − k e y < r o o t . k e y s ( k e y , r o o t . r i g h t ) − − k e y > r o o t . k e y r o o t − − k e y = r o o t . k e y n u l l − − 没 有 找 到 结 果 , r o o t = n u l l s(key,root) = \begin{cases}s(key,root.left)--key<root.key\\s(key,root.right)--key>root.key\\root--key=root.key\\null--没有找到结果,root=null\end{cases} s(key,root)=s(key,root.left)key<root.keys(key,root.right)key>root.keyrootkey=root.keynullroot=null
删除结点

删除结点是二叉排序树中的一个比较复杂的算法, 因为在删除节点后,得到的树依然要满足二叉排序树的形式,所以需要对其删除结点的情况进行讨论,并对其进行一个维护,结点的删除情况一共有三种

  • 当被删除的结点为叶子结点时,直接删除即可

在这里插入图片描述

  • 当被删除的结点有一个子树时,用其孩子结点代替该位置
    在这里插入图片描述

  • 当被删除的结点有两个子树时,就需要选择左子树中关键字最大的结点来代替这个位置

在这里插入图片描述

算法实现

c++实现

数据组织

一个树的结点,采用结构体的形式定义,里面包含一个关键字,存储的数据,以及其左右孩子对应的指针域

#include<iostream>
using namespace std;
using KeyType = int;

template<typename T>
struct Node{
    KeyType key;  //关键字
    T data;  //存储值
    Node<T>* left;  //左孩子指针域
    Node<T>* right;  //右孩子指针域
    Node(KeyType key,T data){
        this->key = key;
        this->data = data;
        left = nullptr;
        right = nullptr;
    }
};

二叉排序树

当前二叉排序树维护一个,结点数量大小,一个存储二叉树结点的根结点,方法有、插入结点、删除结点、查找结点、判断是否为一颗空树,当前结点数量,前、中、后序遍历这几个方法

//这颗二叉树有如下方法
int getSize();   //返回当前树存储结点树
bool insertNode(KeyType key,T data);  //往二叉树中插入结点
Node<E> searchNode(KeyType key);  //查找关键字key对应的Node<E>
bool deleteNode(KeyType key);  //删除二叉树中关键字为key的结点,如果删除成功,则返回true,如果没有该节点则返回false
void prevOrder(); //前序输出二叉树所有关键字,根->左->右
void midOrder(); //中序输出二叉树所有关键字, 左->根->右   对于二叉搜索树,其层序遍历出来的关键字一定是有序的
void lasteOrder(); //后序输出二叉树所有的关键字,左->右->根
bool isEmpty(); //判断当前二叉树是不是一颗空树

主要就是上面一些基本方法,可能在类中还会定义一些辅助方法来帮助这些方法的实现

template<typename E>
class BstTree{

public:
    //构造函数

    BstTree():size(0){
        initRoot();
    }

    BstTree(Node<E> data[],int len):size(0){
        initRoot();
        for (int i = 0; i < len; ++i){
            insertNode(data[i].key,data[i].data);
        }
    }

    BstTree(int key[],int len1,E data[],int len2):size(0){
        if(len1 != len2){
            throw runtime_error("the length of key != the length of data!");
        }else{
            initRoot();
            for (int i = 0; i < len1; ++i){
                insertNode(key[i],data[i]);
            }
        }
    }

    //析构函数
    ~BstTree(){
        deleteBst(root);
    }


public:
    //公有方法

    bool isEmpty(){
        /*
         * 判断一颗树是否为一颗空树
         */
        return size == 0;
    }

    int getSize(){
        /*
         * 获取二叉树当前存储结点数
         */
        return size;
    }

    bool insertNode(KeyType key,E data){
        /*
         * 往二叉树中插入结点
         */
        return insertNode(root,key,data);
    }

    Node<E> searchNode(KeyType key){
        /*
         * 在二叉树中查找关键字为key的结点
         * 如果存在该节点,则返回该结点
         * 否则则返回NULL
         */
        return searchNode(root,key);
    }

    bool deleteNode(KeyType key){
        /*
         * 删除二叉树中关键字为key的结点
         * 如果存在该节点,则成功删除,返回true
         * 不存在则返回false
         */
        return deleteNode(root,key);
    }

    void prevOrder(){
        /*
         * 前序遍历所有结点
         */
        prevOrder(root);
        cout<<endl;
    }

    void midOrder(){
        /*
         * 中序遍历所有结点
         */
        midOrder(root);
        cout<<endl;
    }

    void lasteOrder(){
        /*
         * 后序遍历所有结点
         */
        lasteOrder(root);
        cout<<endl;
    }


private:
    //初始化根结点
    void initRoot(){
        /*
         * 初始化根结点为一颗空树
         */
        this->root = nullptr;
    }

    bool insertNode(Node<E> *&root,KeyType key,E data){
        /*
         * 当结点为空时,则将传入的数值传入当前结点
         * 如果结点不为空时,如果key<root->key 则往左孩子去插入
         *                  如果key>root->key 则往右孩子去插入
         * 插入成功返回true
         * 插入不成功返回false,即key == root->key的时候
         */
        if(root == nullptr){
            root = new Node<E>(key,data);
            size++;
            return true;
        }else{
            if(key < root->key){
                return insertNode(root->left,key,data);
            }else if(key > root->key){
                return insertNode(root->right,key,data);
            }else{
                return false;
            }
        }
    }

    Node<E> searchNode(Node<E> *root,KeyType key){
        /*
         * 查找二叉树中关键字为key的结点
         */
        if(root == nullptr)
            return NULL;
        else{
            if(key < root->data){
                return searchNode(root->left,key);
            }else if(key > root->key){
                return searchNode(root->right,key);
            }else{
                return Node<E>(root->key,root->data);
            }
        }
    }


    bool deleteNode(Node<E> *&root,KeyType key){
        /*
         * 当被删除的节点为叶子结点时,直接删除
         * 当被删除的结点有一个子树时,用子树代替其位置
         * 当被删除的结点左右子树都存在时,则用其左子树中最大的那个结点代替他的位置
         */
        if(root == nullptr)
            return false;
        else{
            if(key < root->key){
                return deleteNode(root->left,key);
            }else if(key > root->key){
                return deleteNode(root->right,key);
            }else{
                delNode(root);
                size--;
                return true;
            }
        }
    }

    void delNode(Node<E> *&root){
        /*
         * root是要被删除的结点
         */
        //开始删除结点,删除结点分为三种情况
        if(root == nullptr){
            return;
        }
        else if(root->left == nullptr && root->right == nullptr){
            delete root;
            root = nullptr;
        }
        else if(root->left != nullptr && root->right != nullptr){
            //首先找到root左子树中最大的结点
            Node<E> *p = root->left,*q;
            if(p->right != nullptr){
                while(p->right != nullptr && p->right->right != nullptr){
                    p = p->right;
                }
                q = p->right;
                p->right = q->left;
                root->key = q->key;
                root->data = q->data;
                delete q;
            }else{
                root->left = p->left;
                root->key = p->key;
                root->data = p->data;
                delete p;
            }
        }
        else if(root->right != nullptr){
            Node<E> *p = root;
            root = root->right;
            delete p;
        }
        else{
            Node<E> *p = root;
            root = root->left;
            delete p;
        }
    }

    //先序遍历
    void prevOrder(Node<E> *root){
        if(root == nullptr)
            return;
        else{
            cout<<root->key<<" ";
            prevOrder(root->left);
            prevOrder(root->right);
        }
    }

    //中序遍历
    void midOrder(Node<E> *root){
        if(root == nullptr)
            return;
        else{
            midOrder(root->left);
            cout<<root->key<<" ";
            midOrder(root->right);
        }
    }

    //后序遍历
    void lasteOrder(Node<E> *root){
        if(root == nullptr)
            return;
        else{
            lasteOrder(root->left);
            lasteOrder(root->right);
            cout<<root->key<<" ";
        }
    }

    void deleteBst(Node<E> *&root){
        /*
         * 销毁二叉树,释放二叉树所有结点空间
         */
        if(root == nullptr)
            return;
        else{
            deleteBst(root->left);
            deleteBst(root->right);
            delete root;
        }
    }

private:
    int size;  //结点数量
    Node<E> *root;  //根结点
};

Java实现

package myTree;

public class MyBstTree<C extends Comparable,E>{

    int size;
    Node<C,E> root;

    MyBstTree(){
        size = 0;
        initRoot();
    }

    public boolean isEmpty(){
        return size == 0;
    }

    public int getSize(){
        return size;
    }

    public void insertNode(C key,E data){
        /**
         * 往二叉搜索树中插入结点
         */
        root =  insertNode(root,key,data);
    }

    private Node<C,E> insertNode(Node<C,E> root,C key,E data){
        if(root == null){
            root = new Node<C,E>(key,data);
            size++;
        }else{
            if(key.compareTo(root.key) < 0){
                root.left = insertNode(root.left,key,data);
            }else if(key.compareTo(root.key) > 0){
                root.right = insertNode(root.right,key,data);
            }
        }
        return root;
    }

    public Node<C,E> searchNode(C key){
        /**
         * 在二叉搜索树中查找关键字为key的结点
         */
        return searchNode(root,key);
    }

    private Node<C,E> searchNode(Node<C,E> root,C key){
        if(root == null)
            return null;
        else{
            if(key.compareTo(root.key) < 0){
                return searchNode(root.left,key);
            }else if(key.compareTo(root.key) > 0){
                return searchNode(root.right,key);
            }else{
                return root;
            }
        }
    }

    public void deleteNode(C key){
        /**
         * 删除二叉搜索树中关键字为key的结点
         */
        root = deleteNode(root,key);
    }

    private Node<C,E> deleteNode(Node<C,E> root, C key) {
        if(root == null)
            return null;
        else{
            if(key.compareTo(root.key) < 0){
                root.left = deleteNode(root.left,key);
            }else if(key.compareTo(root.key) > 0){
                root.right = deleteNode(root.right,key);
            }else{
                size--;
                root = delNode(root);
            }
        }
        return root;
    }

    private Node<C,E> delNode(Node<C,E> root) {
        /**
         * 删除二叉搜索树中的结点
         * 1、当被删除的结点为叶子结点,则直接删除
         * 2、当被删除的结点只有一个孩子结点,则用这个孩子结点代替这个位置
         * 3、当被删除的结点两个孩子都存在的时候,则用其左孩子中的最大值代替它,并维护好左子树
         */
        if(root == null)
            return null;
        else if(root.left == null && root.right == null){
            root = null;
        }
        else if(root.left != null && root.right != null){
            Node<C,E> p = root.left;
            if(p.right != null){
                while(p.right != null && p.right.right != null){
                    p = p.right;
                }
                Node<C,E> q = p.right;
                p.right = q.left;
                root.key = q.key;
                root.data = q.data;
            }else{
                root.left = p.left;
                root.key = p.key;
                root.data = p.data;
            }
        }
        else if(root.right != null){
            root = root.right;
        }
        else{
            root = root.left;
        }
        return root;
    }

    public void prevOrder(){
        /**
         * 前序遍历二叉树
         */
        prevOrder(root);
        System.out.println();
    }

    private void prevOrder(Node<C,E> root) {
        if(root == null)
            return;
        else{
            System.out.print(root.key+" ");
            prevOrder(root.left);
            prevOrder(root.right);
        }
    }

    public void midOrder(){
        /**
         * 中序遍历二叉树
         */
        midOrder(root);
        System.out.println();
    }

    private void midOrder(Node<C,E> root) {
        if(root == null)
            return;
        else{
            midOrder(root.left);
            System.out.print(root.key+" ");
            midOrder(root.right);
        }
    }

    public void lasteOrder(){
        /**
         * 后序遍历二叉树
         */
        lasteOrder(root);
        System.out.println();
    }

    private void lasteOrder(Node<C,E> root) {
        if(root == null)
            return;
        else{
            lasteOrder(root.left);
            lasteOrder(root.right);
            System.out.println(root.key+" ");
        }
    }


    private void initRoot(){
        root = null;
    }


    class Node<C,E>{
        /**
        * 结点定义
        */
        C key;  //关键字
        E data;  //存储的值data
        Node<C,E> left;  //左孩子 
        Node<C,E> right;  //右孩子

        Node(C key,E data){
            this.key = key;
            this.data = data;
            left = null;
            right = null;
        }
    }

    
    //测试数据
    public static void main(String[] args) {
        MyBstTree<Integer,String> myBstTree = new MyBstTree<>();
        myBstTree.insertNode(10,"1");
        myBstTree.insertNode(9,"2");
        myBstTree.insertNode(13,"3");
        myBstTree.insertNode(8,"4");
        myBstTree.insertNode(11,"5");
        myBstTree.insertNode(15,"6");
        myBstTree.midOrder();
        myBstTree.deleteNode(10);
        myBstTree.midOrder();
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值