二叉搜索树

一、概念

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

若它的左子树不为空,则左子树上所有节点的值都小于根节点的值

若它的右子树不为空,则右子树上所有节点的值都大于根节点的值

它的左右子树也分别为二叉搜索树

二叉搜索树中序遍历是有序的结构

如下图所示就是一个二叉搜索树:

Java实现二叉查找树 java二叉搜索树_Java实现二叉查找树

二、搜索树操作

1.查找节点

1.如果根节点的值等于要查找的值,返回value所在的节点

2.如果根节点的值大于要查找的值,那么在其左子树查找

3.如果根节点的值小于要查找的值,那么在其右子树查找

4.没找到返回null

Java实现二叉查找树 java二叉搜索树_子树_02


代码实现:

class Node{
    int val;
    Node left;
    Node right;
    public Node(int val){
        this.val = val;
        left = right = null;
    }
}

public class BSTree {
    private Node root = null;
    //在搜索书中查找val,找到返回val所在的节点,没找到的话返回null
    public Node find(int val){
        if(root == null)
            return null;
        Node cur = root;
        while(cur != null){
            //找到返回节点,如果当前节点值大于val,向搜索树左边遍历,反之向右遍历
            if(cur.val == val){
                return cur;
            }else if(cur.val > val){
                cur = cur.left;
            }else{
                cur = cur.right;
            }
        }
        return null;
    }
}

2.插入节点

插入分为两种情况:

1.如果树为空树,即根 == null,直接插入

Java实现二叉查找树 java二叉搜索树_子树_03


2.如果树不是空树,按照查找逻辑确定插入位置,插入新结点,插入的地方都是叶子节点的位置

Java实现二叉查找树 java二叉搜索树_Java实现二叉查找树_04


代码实现:

//向二叉搜索书中插入节点,插入成功返回true,失败返回false
    public boolean insert(int val){
        if(root == null){
            root = new Node(val);
            return true;
        }
        Node cur = root;
        Node parent = null;
        //搜索适合插入的位置(都是叶子节点的位置)
        while(cur != null){
            parent = cur;
            //不能有值相等的节点
            if(cur.val == val){
                return false;
            }else if(cur.val > val){
                cur = cur.left;
            }else{
                cur = cur.right;
            }
        }
        //如果parent的值大于val插入parent左边,反之插入右边
        Node node = new Node(val);
        if(parent.val > val){
            parent.left = node;
        }else{
            parent.right = node;
        }
        return true;
    }

3.删除节点
设待删除结点为 cur, 待删除结点的双亲结点为 parent
删除节点主要分为三种情况:
1.要删除的节点只有左孩子,即cur.right == null;

  1. cur 是 root,则 root = cur.left
  2. cur 不是 root,cur 是 parent.left,则 parent.left = cur.left
  3. cur 不是 root,cur 是 parent.right,则 parent.right = cur.left

2.要删除的节点只有右孩子,即cur.left == null;

  1. cur 是 root,则 root = cur.right
  2. cur 不是 root,cur 是 parent.left,则 parent.left = cur.right
  3. cur 不是 root,cur 是 parent.right,则 parent.right = cur.right

3.要删除的是根节点,即cur.left != null && cur.right != null;
有两种简单的解决思路:
1.找到以此节点为根的左子树的最右节点,交换根节点与最右节点,再删除交换到最右节点处的根节点
2.找到右子树的最左节点,同样交换两个节点再删除

代码实现:
找到要删除节点的位置

//找到要删除的节点
    public boolean remove(int val){
        if(root == null){
            return false;
        }
        Node cur = root;
        //cur的后继节点,保存cur的父节点的位置
        Node parent = null;
        //找到节点在搜索树中的位置
        while(cur != null){
            if(cur.val == val){
                break;
            }else if(cur.val > val){
                parent = cur;
                cur = cur.left;
            }else{
                parent = cur;
                cur = cur.right;
            }
        }
        //判断节点是否存在
        if(cur == null){
            return false;
        }
        //将此节点从搜索树中删除
        removeNode(parent, cur);
        return true;
    }

删除这个节点

//删除的过程
    public void removeNode(Node parent, Node cur){
        //1.cur左子树为空(包括cur为叶子节点,此时cur.right为null而已)
        //2.cur右子树为空
        //3.cur左右子数均不为空
        if(cur.left == null){
            if(cur != root){
                //cur的子树应该连接在父节点的方向
                if(parent.left == cur){
                    parent.left = cur.right;
                }else{
                    parent.right = cur.right;
                }
            }else{
                root = cur.right;
            }
        }else if(cur.right == null){
            if(cur != root){
                if(parent.left == cur){
                    parent.left = cur.left;
                }else{
                    parent.right = cur.left;
                }
            }else{
                root = cur.left;
            }
        }else{//cur左右均不为空
            parent = cur;
            Node next = cur.left;
            //找到左子树的最右节点
            while(next.right != null){
                parent = next;
                next = next.right;
            }
            //将左子树的最右节点的值赋给cur
            cur.val = next.val;
            //删除左子树最右节点
            if(parent.left == next){
                parent.left = next.left;
            }else{
                parent.right = next.left;
            }
        }
    }