二叉搜索树

目录

二叉搜索树

操作-查找

操作-插入

操作-删除

性能分析


二叉搜索树

二叉搜索树又称二叉排序树,它要么是一棵空树,要么是具有以下性质的二叉树:

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

二叉搜索树_第1张图片

如图就是一棵二叉搜索树.

下面,我们来实现几个二叉搜索树的操作.

操作-查找

查找val是否在当前搜索树当中,根据二叉搜索树的特点,如果根节点的val == 查找val,则直接返回根节点;如果根节点的val > 查找val,就去根节点的左子树里去找;如果根节点的val < 查找的val,就去根节点的右子树里去找,以此类推,没找到就返回null.

 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 key){
        //根节点为空,就直接插入到根节点的位置
        if (root == null){
            root = new TreeNode(key);
            return true;
        }
        //parent用来记录cur的prev
        TreeNode parent = null;
        TreeNode cur = root;
        //cur==null就说明要进行插入了
        while(cur != null){
            //当前节点的值大于key,就往当前节点的左子树走
            if (cur.val > key){
                parent = cur;
                cur = cur.left;
            }else if (cur.val < key){
                parent = cur;
                cur = cur.right;
            }else {
                //插入相同的元素直接返回false
                return false;
            }
        }
        //代码走到这里cur==null
        //此时比较key和parent的val大小来判断
        //往左子树插入还是右子树插入
        TreeNode node = new TreeNode(key);
        if (parent.val > key){
            parent.left = node;
        }else {
            parent.right = node;
        }
        return true;
    }

操作-删除

删除的逻辑比较复杂,分为多种情况.

设待删除的节点为cur,待删除的双亲结点为parent.

1.cur.left == null

  • cur是root,则root = cur.right
  • cur不是root,cur是parent的left.则parent.left=cur.right
  • cur不是root,cur是parent.right,则parent.right=cur.right

2.cur.right == null

  • cur是root,则root = cur.left
  • cur不是root,cur是parent的right,则parent.right=cur.left
  • cur不是root,cur是parent的left,则parent.left = cur.left

3.cur.left!=null && cur.right!=null

此种情况,要使用替罪羊法进行删除.即在它的右子树中找到最左边的结点(cur右边最小的结点)或者是在它的左子树中找到最右边的结点(cur左边最大的结点),用找到的值来填补到待删除结点中,此时问题就转换为删除替罪结点了.

二叉搜索树_第2张图片

比如要删除12.我们要找到9或者是13来代替12,然后在将9或者13删除掉.

因为9或者13的特殊性,9没有右子树,13没有左子树,所有9或者13的删除就可以使用前两种情况.

二叉搜索树_第3张图片二叉搜索树_第4张图片

public void remove(int key){

        TreeNode parent = null;
        TreeNode cur = root;
        while(cur != null){
            if (cur.val == key){
                removeNode(parent,cur);
                return;
            }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;
            if (targetParent.left == target){
                targetParent.left = target.right;
            }else {
                //待删除节点的右边可能没有左子树
                //此时cur.right就是替罪羊
                targetParent.right = target.right;
            }
        }
    }

性能分析

最优情况下,二叉搜索树为完全二叉树,其平均比较次数为logN

最坏情况下,二叉搜索树退化为单支树,其平均比较次数为N/2

你可能感兴趣的:(数据结构,算法)