Java中的二叉搜索树

二叉搜索树

概念:
二叉搜索树又称二叉排序树,他或是一颗空树,或是具有以下性质的一颗树

  • 若它的左子树不为空,则左子树上所有节点的值都小于根结点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也都分别是二叉搜索树
  • 二叉搜索树中不存在值相同的两个节点

接下来完成二叉搜索树的常见操作
搜索树的结构:

static class Node{
        public int key;
        public int value;
        public Node left;
        public Node right;
        public Node(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }

相关操作的完成过程写在代码的注释中:

public class BinarySearchTree {
    static class Node{
        public int key;
        public int value;
        public Node left;
        public Node right;

        public Node(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }

    private Node root = null;

    //查找元素操作
    public Node find(int key) {
        //先让cur指向根结点
        Node cur = root;
        //开始循环  当cur指向空时就跳出
        while(cur != null) {
            //如果比较值小于节点值则向节点的左子树寻找
            if(key < cur.key) {
                cur = cur.left;
            //如果比较值大于节点值则向节点的右子树寻找
            }else if (key > cur.key) {
                cur = cur.right;
            //走到这里就说明val == cur.key
            }else{
                return cur;
            }
        }
        //运行到这里时就说明没有找到相关值,则返回空
        return null;
    }

    //插入元素操作
    //进行插入操作时,需要查找到待插入位置,并且记录此此节点的父结点
    public Node insert(int key,int value) {
        //如果当前树是空树,那么就让root指向新的节点
        if(root == null) {
            root = new Node(key,value);
            return root;
        }
        //此时利用cur找到应插入的位置
        //利用prev记录cur的父结点
        Node cur = root ;
        Node prev = null;
        //开始寻找应插入的位置
        while(cur != null) {
            //如果将插入的值小于当前节点的值,则向当前节点的左节点寻找
            if(key < cur.key) {
                prev = cur;
                cur = cur.left;
                //如果将插入的值大于当前节点的值,则向当前节点的右节点寻找
            }else if( key > cur.key) {
                prev = cur;
                cur = cur.right;
            }else {
                //找到了key相同的元素
                //此时处理情况有很多种
                //可以直接使插入失败
                //也可以把当前节点的value改成新的value
                cur.value = value;
                return cur;
            }
        }
        //当循环结束时,cur 就为 null了
        //经过上面的查找一定能找到合适的位置
        //要把新节点插到prev的下面
        Node newNode = new Node(key,value);
        if(newNode.value < prev.value) {
            prev.left = newNode;
        }else {
            prev.right = newNode;
        }
        return newNode;
    }
	//删除元素操作
    public void remove(int key) {
        //先找到要删除的节点,同时记录该节点的父结点位置
        Node cur = root;
        Node parent = null;
        while(cur != null) {
            //跟上面相同的操作
            if(key < cur.key) {
                parent = cur;
                cur = cur.left;
            }else if( key > cur.key) {
                parent = cur;
                cur = cur.right;
            }else{
                //走到这里代表已经找到要删除的位置,进入辅助方法
                removeNode(cur,parent);
                return;
            }
        }
        return ;
    }
	//删除元素辅助方法
    private void removeNode(Node cur, Node parent) {
        //搜索树的删除操作需要考虑的要素较多
        //1.要删除的节点没有左子树
        //2.要删除的节点没有右子树
        //3.要删的节点左右子树都存在
        if(cur.left == null) {
            //如果要删除的节点没有左子树
            if(cur == root) {
                //如果要删除的节点为根结点
                //因为此时的根结点没有左子树
                cur = cur.right;
            }else if(cur == parent.left){
                //如果此时要删除的节点为父节点的左子树
                parent.left = cur.right;
            }else if(cur == parent.right) {
                //如果此时要删除的节点为父结点的右子树
                parent.right = cur.right;
            }
        }else if(cur.right == null) {
            //如果要删除的节点没有右子树
            if(cur == root) {
                cur = cur.left;
            }else if (cur == parent.left) {
                parent.left = cur.left;
            }else {
                parent.right = cur.left;
            }
        }else{
            //如果要删除的节点左右子树都存在
            //那么找到当前节点右子树的最左侧的节点
            //找到的节点之比当前节点的值大,而比当前节点右子树中其他节点的值窦骁
            Node scapeGoat = cur.right;
            Node scapeGoatParent = cur;
            //下面这个条件是判断寻找的节点存在左子树,如果不存在,则当前节点就是最左侧的节点
            while(scapeGoat.left != null){
                scapeGoatParent = scapeGoat;
                scapeGoat = scapeGoat.left;
            }
            //将当前节点的key和value更新为scapeGoat的相关值
            cur.key = scapeGoat.key;
            cur.value = scapeGoat.value;
            //然后删除scapeGoat节点
            if(scapeGoat == scapeGoatParent.left) {
                scapeGoatParent.left = scapeGoat.right;
            }else{
                scapeGoatParent.right = scapeGoat.right;
            }
        }
    }
}

总结:
二叉树的基本特性是:针对树上的任意节点,其左子树的所有节点都小于根结点,右子树的所有节点都大于根结点,并且一般不允许存在两个相同的值。

插入、查找、删除操作的时间复杂度是O(N),如果考虑到平衡性(AVL,红黑树)则为O(LogN)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值