二叉搜索树

目录

🐉今日良言:要诚恳,要坦然,要慷慨,要宽容,要有平常心。

🐳1.介绍

🐇2.实现步骤

🐂3.完整代码


🐉今日良言:要诚恳,要坦然,要慷慨,要宽容,要有平常心。

🐳1.介绍

在创建二叉搜索树之前,先来了解一下什么是二叉搜索树?

二叉搜索树又叫二叉排序树,它或者是一棵空树,或者具有以下性质:

左子树不为空,则左子树节点的值都小于根节点的值

右子树不为空,则右子树节点的值都大于根节点的值

左右子树也是一棵二叉搜索树

如下图:

🐇2.实现步骤

这里主要实现三部分代码:查找数据、添加节点、删除节点

查找数据:

当一棵二叉搜索树构建完成后,如何在这棵树中查找数据呢?

其实我们可以类比二叉树的三种遍历,首先创建一个辅助节点cur,让cur指向根节点,先判断cur的值与查找数据之间的大小关系,如果查找数据的值大于cur的值,说明该数据可能在cur的右子树,修改cur为:cur = cur.right

,如果查找数据的值小于cur的值,说明该数据可能在cur的左子树,修改cur为:cur = cur.left;

如果找到直接到返回cur即可,不难看出,上述操作是一个循环,直到cur为空,结束循环。没找到就返回null。

图解:以找6为例:

 查找操作代码:

  // 查找val值
    public TreeNode search(int val) {
        TreeNode cur = root;
        while (cur != null) {
            if (cur.val < val) {
                cur = cur.right;
            } else if (cur.val > val) {
                cur = cur.left;
            } else {
                return cur;
            }
        }
        return null;
    }

添加节点:

 首先,如果当前二叉搜索树根节点为空,则直接创建一个节点作为根节点即可。

其次,如果二叉搜索树根节点不为空。先想到的都是找到待插入数据的位置,那么如何找到这个位置呢?显而易见,通过遍历就可以找到,以下图为例:

如果想添加一个节点4

创建一个辅助节点cur,从根节点开始,cur != null 进行循环查找,

如果查找数据的值大于cur的值,说明该数据可能在cur的右子树,修改cur为:cur = cur.right

如果查找数据的值小于cur的值,说明该数据可能在cur的左子树,修改cur为:cur = cur.left;

如果找到直接到返回false即可,因为一棵二叉搜索树中的数据值不同,

当循环结束后,此时cur的位置如下图:

 所以,应当将节点4插入到3节点的右子树,很明显,如果仅仅只靠一个辅助节点cur,是无法完成添加节点操作的,所以,需要再创建一个辅助节点parent,记录上一次的cur值,那么,在每次cur值被修改之前,先将当前的cur赋给parent,当cur为空时,判断当前parent值与待插入节点值的大小关系,从而确定待插入节点的位置。

添加节点代码:

    // 添加结点
    public boolean insert(int val) {
        if (root == null) {
            root = new TreeNode(val);
            return true;
        }
        TreeNode cur = root;
        TreeNode parent = null;
        while (cur != null) {
            if (cur.val < val) {
                parent = cur;
                cur = cur.right;
            } else if (cur.val > val) {
                parent = cur;
                cur = cur.left;
            } else {
                // 二叉搜索树不可能相同,返回false
                return false;
            }
        }
        // 此时判断当前val值和parent的大小关系
        TreeNode node = new TreeNode(val);
        if (val < parent.val) {
           parent.left = node;
        } else {
            parent.right = node;
        }
        return true;
    }

 删除节点:

删除节点操作比较复杂,删除节点也需要两个辅助节点,仅靠一个是不足以完成删除操作的,创建一个parent和cur辅助节点,cur != null 开始循环,如果cur.val == key(待删除节点值),就进行删除操作,如果小于或者大于与添加节点操作相同,parent先记录当前cur,然后修改cur值。

具体删除操作:

根据cur的左右子树是否为空,可以分为三种情况:

1.cur.left == null   

此时,先判断cur是不是根节点,如果是的话,只需要修改root = cur.right;即可完成删除操作。如果不是根节点,判断cur是parent的左孩子节点还是右孩子节点。

如果是左孩子节点,直接让parent.left = cur.right即可。如图:

如果是parent的右孩子节点,直接让parent.right = cur.right即可。如图:

2.如果cur.right == null

 此时,先判断cur是不是根节点,如果是,则修改root = cur.left,即可完成删除操作,如果不是,

判断cur是parent的左孩子节点还是右孩子节点。

如果是左孩子节点,直接让parent.left = cur.left即可。如图:

 

如果是右孩子节点,直接让parent.right = cur.left即可。如图:

3.如果cur的左右孩子节点都不为空

此时,有两种删除方法:

第一种,找cur的右子树的最小值,将该值作为cur的新值,然后删除这个最小值节点。

第二种,找cur的左子树的最大值,将该值作为cur的新值,然后删除这个最大值节点。

这里采用的是第一种删除方法。由于要删除最后找到的那个节点,所以说,这里需要再创建两个辅助节点,targetParent的初值为cur,target的初值为cur.right,然后开始遍历找到target的左孩子节点为空的节点,每次让targetParent记录上一次的target.

当找到这个节点后,首先修改cur的值:cur.val = target.val  ,然后判断target是targetParent的左孩子还是右孩子节点。

如果是右孩子节点,targetParent.right = target.right; 如图:

如果是左孩子节点,targetParent.left = target.right; 如图:

🐂3.完整代码

public class BinarySearchTree {
    static class TreeNode {
        public int val;
        public TreeNode left;
        public TreeNode right;
        public TreeNode(int val) {
            this.val = val;
        }
    }
    public TreeNode root = null;
    public static void main(String[] args) {
        int[] arr = {5,3,4,1,7,8,2,6,0,9};
        BinarySearchTree binarySearchTree = new BinarySearchTree();
        for (int i = 0; i < arr.length; i++) {
            binarySearchTree.insert(arr[i]);
        }
        binarySearchTree.inorder(binarySearchTree.root);
        binarySearchTree.remove(7);
        binarySearchTree.inorder(binarySearchTree.root);
    }
    // 中序遍历
    public void inorder(TreeNode root) {
        if (root == null) {
            return;
        }
        inorder(root.left);
        System.out.print(root.val+" ");
        inorder(root.right);
    }
    // 查找val值
    public TreeNode search(int val) {
        TreeNode cur = root;
        while (cur != null) {
            if (cur.val < val) {
                cur = cur.right;
            } else if (cur.val > val) {
                cur = cur.left;
            } else {
                return cur;
            }
        }
        return null;
    }
    // 添加结点
    public boolean insert(int val) {
        if (root == null) {
            root = new TreeNode(val);
            return true;
        }
        TreeNode cur = root;
        TreeNode parent = null;
        while (cur != null) {
            if (cur.val < val) {
                parent = cur;
                cur = cur.right;
            } else if (cur.val > val) {
                parent = cur;
                cur = cur.left;
            } else {
                // 二叉搜索树不可能相同,返回false
                return false;
            }
        }
        // 此时判断当前val值和parent的大小关系
        TreeNode node = new TreeNode(val);
        if (val < parent.val) {
           parent.left = node;
        } else {
            parent.right = node;
        }
        return true;
    }
    // 删除节点
    public void remove(int key) {
        TreeNode cur = root;
        TreeNode parent = null;
        while (cur != null) {
            if (cur.val == key) {
                removeNode(parent,cur);
            } else if (cur.val < key) {
                parent = cur;
                cur = cur.right;
            } else {
                parent = cur;
                cur = cur.left;
            }
        }
    }
    private void removeNode(TreeNode parent,TreeNode cur) {
        // 根据左右子树是否为空分三种情况
        if (cur.left == null) {
            // 判断是不是根节点
            if (cur == root) {
                root = cur.right;
            } else if (cur == parent.left) {
                parent.left = cur.right;
            } else {
                parent.right = cur.right;
            }
        } else if (cur.right == null) {
            if (cur == root) {
                root = cur.left;
            } else if (cur == parent.left) {
                parent.left = cur.left;
            } else {
                parent.right = cur.left;
            }
        } else {
            // 替罪羊删除法
            TreeNode target = cur.right;
            TreeNode targetParent = cur;
            while (target.left != null) {
                targetParent = target;
                target = target.left;
            }
            cur.val = target.val;
            // 开始判断target是targetParent的左孩子还是右孩子
            if (target == targetParent.left) {
                targetParent.left = target.right;
            } else {
                targetParent.right = target.right;
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值