八大数据结构——二叉搜索树(七)

八大数据结构——二叉搜索树(七)


树是数据结构中非常重要的一项,有关树的数据结构有许多种,本文重点研究的是二叉搜索树。

二叉搜索树定义:

二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。
在这里插入图片描述

树结构,顾名思义,就像一颗树一样,从根开始,一直往上不断有分支,只不过这里的数是倒过来的,根在最上面。

二叉搜索树就是其中一种,它有以下 特性
1.它是一个二叉树,二叉树就是一个父节点,它最多只有两个子节点。
2.这个树上任意一个节点,左子节点一定小于父节点。
3.这个树上任意一个节点,右子节点一定大于父节点。

优点:
1.有链表的快速插入与删除操作的特点。
2.又有数组快速查找的优势。

缺点:
1.二叉搜索树的构造与插入顺序有关。
2.某些情况下,有可能会退化成链表,且指针还浪费空间。

适用场景:
大量数据的存储和操作。它的构建,插入,删除,查询的复杂度都在O( log ⁡ 2 \log_2 log2N)~O(N),之间。

Java代码实现

下面通过Java来实现一个二叉搜索树,基于jdk1.8。
1.首先把树节点定义出来。

class BSTNode<T extends Comparable>{
    public T val;//存储数据
    public BSTNode<T> leftNode;//左子节点
    public BSTNode<T> rightNode;//右子节点
}

2.定义二叉搜索树的属性。

    private BSTNode<T> root;//根节点
    private int size;//记录已存储元素个数

3.构造方法和一些常用方法。

    public BinarySearchTree(){
        root = null;
        size = 0;
    }

    public BinarySearchTree(T val){
        root = new BSTNode<>();
        root.val = val;
        size = 1;
    }

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

    public int size(){return size;}

4.查询操作。查询从根节点开始,通过不断比较一直往下走,大于当前节点就往右子树的方向走,小于当前节点就往左子树的方向走。

    private BSTNode<T> find(T val){
        if(root==null||val==null){
            return null;
        }
        BSTNode<T> node = root;
        while (node!=null){
            if(node.val.compareTo(val)==0){
                return node;
            }else if(node.val.compareTo(val)>0){
                node = node.leftNode;
            }else {
                node = node.rightNode;
            }
        }
        return null;
    }

    private BSTNode<T> parent(BSTNode<T> current){
        if(root==null||current==null){
            return null;
        }
        BSTNode<T> node = root;
        while (node!=null){
            if(node.leftNode==current||node.rightNode==current){
                return node;
            }else if(node.val.compareTo(current.val)>0){
                node = node.leftNode;
            }else {
                node = node.rightNode;
            }
        }
        return null;
    }

//    返回父节点的值
    public T parent(T val){
        BSTNode<T> node = parent(find(val));
        return node==null?null:node.val;
    }

//    返回左子节点的值
    public T leftNode(T val){
        BSTNode<T> node = find(val);
        return node!=null&&node.leftNode!=null?node.leftNode.val:null;
    }

//    返回右子节点的值
    public T rightNode(T val){
        BSTNode<T> node = find(val);
        return node!=null&&node.rightNode!=null?node.rightNode.val:null;
    }

//    返回根节点的值
    public T getRoot(){
        return root==null?null:root.val;
    }
    //    查询是否包含数据
    public boolean contain(T val){
        return find(val)!=null;
    }

5.插入操作,插入同查询的逻辑一样,插入是插入到树的最下方,成为叶子节点,并且不允许有重复的值。

//    插入数据
    public boolean insert(T val){
//        如果val为null直接返回
        if(val==null){
            return false;
        }
//        如果root为null,那新建一个节点,并让保存为root
        if(root==null){
            root = new BSTNode<>();
            root.val=val;
            size++;
            return true;
        }
//        root不为null,那就通过循环找到正确的插入位置
        BSTNode<T> node = root;
        while (true){
//            如果val与已有值相等,则不允许插入
            if(node.val.compareTo(val)==0){
                return false;
            }
            //            val值小于当前节点,则插入到左子树
            if(node.val.compareTo(val)>0){
//                如果左子节点为空,则插入到此处
                if(node.leftNode==null){
                    BSTNode<T> leftNode = new BSTNode<>();
                    leftNode.val = val;
                    node.leftNode = leftNode;
                    size++;
                    return true;
                }else {
//                    不为空继续往下找
                    node = node.leftNode;
                }
//                val值大于当前节点,则插入到右子数
            }else {
//                如果右子节点为空,则插入到此处
                if(node.rightNode==null){
                    BSTNode<T> rightNode = new BSTNode<>();
                    rightNode.val = val;
                    node.rightNode = rightNode;
                    size++;
                    return true;
                }else {
//                    不为空继续往下找
                    node = node.rightNode;
                }
            }
        }
    }

6.删除操作,删除比较复杂一点。我们将要删除的节点分为三种情况:
1.度为0,就是子节点个数为0 。
2.度为1,就是子节点个数为1 。
3.度为2,就是子节点个数为2 。

具体操作:
1.找到要删除的节点。
2.如果要删除的节点度为2,那么
(1)先找到要删除节点的左子树中,最大的节点。
(2)交换要删除节点与其左子树中,最大的节点,的值。
(3)删除其左子树中,最大的节点。
3.如果要删除的节点度为0或1,那么
(1)获取要删除节点的父节点。
(2)获取要删除节点的子节点。
(3)如果要删除节点的父节点为空,那说明要删除的节点为根节点,那么将根节点的指针指向要删除节点的子节点。
(4)如果要删除节点的父节点不为空,那么判断要删除节点是其父节点的左子节点还是右子节点,然后将其父节点的左子指针或右子指针指向要删除节点的子节点。

//        要删除的节点有三种情况,度为2,度为1和度为0的
        if(node.leftNode!=null&&node.rightNode!=null){
//            度为2的节点,删除步骤为
//            1.找到要删除节点的左子数中最大节点。
//            2.替换待删除节点的值为这个最大节点值
//            3.将这个最大节点删除
            BSTNode<T> maxNode = node.leftNode;
            while (maxNode.rightNode!=null){
                maxNode = maxNode.rightNode;
            }
            remove(maxNode.val);
            node.val=maxNode.val;
        }else {
//            度为1和度为2的节点一起处理,分两种情况。
//            1.要删除的节点为根节点。
//            2.要删除的节点为非根节点。
            BSTNode<T> parent = parent(node);
            BSTNode<T> child = node.leftNode == null?node.rightNode:node.leftNode;
//            根节点无父节点,直接将根节点指向子节点即可。
            if(parent==null){
                root = child;
            }else {
//                非根节点,找到它是父节点的左子节点,还是右子节点,然后通过修改父节点指针即可。
                if(parent.leftNode!=null&&parent.leftNode==node){
                    parent.leftNode = child;
                }else {
                    parent.rightNode = child;
                }
            }
        }
        size--;
        return true;
    }

完整代码

//二叉搜索树
public class BinarySearchTree<T extends Comparable> {
    private BSTNode<T> root;//根节点
    private int size;//记录已存储元素个数

    public BinarySearchTree(){
        root = null;
        size = 0;
    }

    public BinarySearchTree(T val){
        root = new BSTNode<>();
        root.val = val;
        size = 1;
    }

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

    public int size(){return size;}

    private BSTNode<T> find(T val){
        if(root==null||val==null){
            return null;
        }
        BSTNode<T> node = root;
        while (node!=null){
            if(node.val.compareTo(val)==0){
                return node;
            }else if(node.val.compareTo(val)>0){
                node = node.leftNode;
            }else {
                node = node.rightNode;
            }
        }
        return null;
    }

    private BSTNode<T> parent(BSTNode<T> current){
        if(root==null||current==null){
            return null;
        }
        BSTNode<T> node = root;
        while (node!=null){
            if(node.leftNode==current||node.rightNode==current){
                return node;
            }else if(node.val.compareTo(current.val)>0){
                node = node.leftNode;
            }else {
                node = node.rightNode;
            }
        }
        return null;
    }

//    返回父节点的值
    public T parent(T val){
        BSTNode<T> node = parent(find(val));
        return node==null?null:node.val;
    }

//    返回左子节点的值
    public T leftNode(T val){
        BSTNode<T> node = find(val);
        return node!=null&&node.leftNode!=null?node.leftNode.val:null;
    }

//    返回右子节点的值
    public T rightNode(T val){
        BSTNode<T> node = find(val);
        return node!=null&&node.rightNode!=null?node.rightNode.val:null;
    }

//    返回根节点的值
    public T getRoot(){
        return root==null?null:root.val;
    }

//    插入数据
    public boolean insert(T val){
//        如果val为null直接返回
        if(val==null){
            return false;
        }
//        如果root为null,那新建一个节点,并让保存为root
        if(root==null){
            root = new BSTNode<>();
            root.val=val;
            size++;
            return true;
        }
//        root不为null,那就通过循环找到正确的插入位置
        BSTNode<T> node = root;
        while (true){
//            如果val与已有值相等,则不允许插入
            if(node.val.compareTo(val)==0){
                return false;
            }
            //            val值小于当前节点,则插入到左子树
            if(node.val.compareTo(val)>0){
//                如果左子节点为空,则插入到此处
                if(node.leftNode==null){
                    BSTNode<T> leftNode = new BSTNode<>();
                    leftNode.val = val;
                    node.leftNode = leftNode;
                    size++;
                    return true;
                }else {
//                    不为空继续往下找
                    node = node.leftNode;
                }
//                val值大于当前节点,则插入到右子数
            }else {
//                如果右子节点为空,则插入到此处
                if(node.rightNode==null){
                    BSTNode<T> rightNode = new BSTNode<>();
                    rightNode.val = val;
                    node.rightNode = rightNode;
                    size++;
                    return true;
                }else {
//                    不为空继续往下找
                    node = node.rightNode;
                }
            }
        }
    }

//    删除数据
    public boolean remove(T val){
//        先通过循环找到要删除的节点
        BSTNode<T> node = root;
        while (node!=null&&node.val.compareTo(val)!=0) {
            if(node.val.compareTo(val)>0){
                node = node.leftNode;
            }else if(node.val.compareTo(val)<0){
                node = node.rightNode;
            }
        }
//        如果找不到则返回false
        if(node==null){
            return false;
        }
//        要删除的节点有三种情况,度为2,度为1和度为0的
        if(node.leftNode!=null&&node.rightNode!=null){
//            度为2的节点,删除步骤为
//            1.找到要删除节点的左子数中最大节点。
//            2.替换待删除节点的值为这个最大节点值
//            3.将这个最大节点删除
            BSTNode<T> maxNode = node.leftNode;
            while (maxNode.rightNode!=null){
                maxNode = maxNode.rightNode;
            }
            remove(maxNode.val);
            node.val=maxNode.val;
        }else {
//            度为1和度为2的节点一起处理,分两种情况。
//            1.要删除的节点为根节点。
//            2.要删除的节点为非根节点。
            BSTNode<T> parent = parent(node);
            BSTNode<T> child = node.leftNode == null?node.rightNode:node.leftNode;
//            根节点无父节点,直接将根节点指向子节点即可。
            if(parent==null){
                root = child;
            }else {
//                非根节点,找到它是父节点的左子节点,还是右子节点,然后通过修改父节点指针即可。
                if(parent.leftNode!=null&&parent.leftNode==node){
                    parent.leftNode = child;
                }else {
                    parent.rightNode = child;
                }
            }
        }
        size--;
        return true;
    }

//    查询是否包含数据
    public boolean contain(T val){
        return find(val)!=null;
    }
}
class BSTNode<T extends Comparable>{
    public T val;//存储数据
    public BSTNode<T> leftNode;//左子节点
    public BSTNode<T> rightNode;//右子节点
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值