java - 数据结构 - 二叉排序树

二叉排序树(BST)

任何一个节点, 左边的节点值都比他小,右边的都比他大,中序遍历得到的是从小到大的数列。

可以用来排序和快速查找。

1. 添加

从根节点向下寻找,比当前节点小就向左,比当前节点大就向右,直到最低端,然后称为新的叶子节点(没有孩子节点的节点)。

2. 查询

从根节点向下寻找,比当前节点小就向左,比当前节点大就向右,相等时就找到了

3. 删除(主要难点)

3种情况

1. 没有孩子节点

叶子节点可以直接删掉,用null代替其原来位置

2. 有一个孩子

用孩子节点代替自己。

3. 有两个孩子

用右孩子的最小叶子节点,或者左孩子的最大叶子节点代替自己,然后删掉那个叶子节点

本质就是找个跟自己中序遍历时挨着的节点代替自己,这样不会影响整体结构。

平衡二叉树(以后再整理=。=)的继承节点是找自己右孩子的左叶子节点,所以这里就用右孩子的左叶子节点代替了。

package tree;

public class BinarySortTree {

    Node root;

    public BinarySortTree(){
        root = null;
    }

    //添加节点
    public void add(int num){
        if(root == null) {
            root = new Node(num);
        }
        else{
            Node n = new Node(num);
            root.add(n);
        }
    }

    //中序遍历 (按顺序从小到大)
    public void show(){
        if(root == null){
            System.out.println("树为空");
        }
        else{
            root.show();
        }
    }

    //发现节点
    public Node findNode(int num){
        if(root == null){
            System.out.println("树为空");
            return null;
        }
        else{
            return root.findNode(num);
        }
    }

    //发现父节点
    public Node findParentNode(int num){
        if(root == null){
            System.out.println("树为空");
            return null;
        }
        else{
            return root.findParentNode(num);
        }
    }

    //发现以某个节点为根节点的子树的最小叶子节点(用来找继承者)
    public Node findSubTreeMin(Node node){
        //因为只是为了找最小,每个节点左边的都比他小,所以只遍历左边
        //不过如果有重复值,按照插入的规则会在左边,会继续向左找,保证找到的是叶子节点。
        if(node.left == null){
            return node;
        }
        else{  //左子树存在,说明有更小的值,继续向左找
            return findSubTreeMin(node.left);
        }
    }

    //删除节点
    public void delete(int num){
        if(root == null){
            System.out.println("树为空");
            return;
        }
        else{
            Node targetNode =  findNode(num);
            if (targetNode == null) {
                System.out.println("没有发现目标节点");
                return;
            }
            Node parentNode = findParentNode(num);
            if (parentNode == null) {   //没有父亲节点,说明是root
                System.out.println("目标节点是root");
                if(root.left != null && root.right == null){ //左节点不为空, 右节点为空
                    root = root.left;
                    return;
                }
                else if(root.left == null && root.right != null){ //左节点不为空, 右节点为空
                    root = root.right;
                    return;
                }
                else if(root.left == null && root.right == null){ //左右都为空
                    root = null;
                    return;
                }
                //3.两个孩子, 用左边最大的叶子节点或者右边最小的叶子节点代替自己 (这里写的是用右边最小的代替)
                //**********注意,如果这里是用右边最小代替,插入的时候重复值要插在左边,不然会导致找到的不是叶子节点,出现问题。
                //上面写的方法是发现子树的最小节点,这里是发现目标右边子树最小的节点,所以参数是root.right
                if(root.left != null && root.right != null){
                    Node rightMinNode = findSubTreeMin(targetNode.right);
                    int temp = rightMinNode.value;
                    delete(temp);
                    root.value = temp;

                }
            }

            else{ // target不是根节点
                // 1. 叶子节点, 2.一个孩子, 3,两个孩子
                //1. 叶子节点, 用null代替自己
                if(targetNode.left == null && targetNode.right == null){ //左右都为空 (叶子节点)
                    //判断是父节点的左孩子还是右孩子,然后置空;
                    if(parentNode.left != null && parentNode.left == targetNode){
                        parentNode.left = null;
                        return;
                    }
                    else if(parentNode.right != null && parentNode.right == targetNode){
                        parentNode.right = null;
                        return;
                    }
                }
                //2.一个孩子, 用孩子代替自己
                if(targetNode.left != null && targetNode.right == null){ //有左孩子没有右孩子,把左孩子放在自己当前位置
                    //判断是父节点的左孩子还是右孩子,然后放置
                    if(parentNode.left != null && parentNode.left == targetNode){
                        parentNode.left = targetNode.left;
                        return;
                    }
                    else if(parentNode.right != null && parentNode.right == targetNode){
                        parentNode.right = targetNode.left;
                        return;
                    }
                }
                if(targetNode.left == null && targetNode.right != null){ //有右孩子没有左孩子,把右孩子放在自己当前位置
                    //判断是父节点的左孩子还是右孩子,然后放置
                    if(parentNode.left != null && parentNode.left == targetNode){
                        parentNode.left = targetNode.right;
                        return;
                    }
                    else if(parentNode.right != null && parentNode.right == targetNode){
                        parentNode.right = targetNode.right;
                        return;
                    }
                }
                //3.两个孩子, 用左边最大的叶子节点或者右边最小的叶子节点代替自己 (这里写的是用右边最小的代替)
                //上面写的方法是发现子树的最小节点,这里是发现目标右边子树最小的节点,所以参数是targetNode.right
                if(targetNode.left != null && targetNode.right != null){
                    Node rightMinNode = findSubTreeMin(targetNode.right);
                    //判断是父节点的左孩子还是右孩子,然后放置
                    if(parentNode.left != null && parentNode.left == targetNode){
                        int temp = rightMinNode.value;
                        delete(temp);
                        parentNode.left.value = temp;
                        return;
                    }
                    else if(parentNode.right != null && parentNode.right == targetNode){
                        int temp = rightMinNode.value;
                        delete(temp);
                        parentNode.right.value = temp;
                        return;
                    }
                }
            }
        }
    }


    public static void main(String[] args){
        BinarySortTree bst = new BinarySortTree();
        bst.add(5);
        bst.add(4);
        bst.add(6);
        bst.add(2);
        bst.add(8);
        bst.add(3);
        bst.add(9);
        bst.add(1);
        bst.add(7);

        bst.add(5);
        bst.add(4);
        bst.add(6);




        System.out.println("中序遍历");
        bst.show();
        System.out.println();

        bst.delete(5);  //删除一个节点
        bst.show();



    }

    public class Node{
        public int value;
        public Node left;
        public Node right;

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

        public void add(Node n){
            if(n == null){
                return;
            }
            else{
                if(n.value <= this.value){  //相等的放在左边
                    if(this.left == null){
                        this.left = n;
                    }
                    else{
                        this.left.add(n);
                    }
                }
                else{  // >=
                    if(this.right == null){
                        this.right = n;
                    }
                    else{
                        this.right.add(n);
                    }
                }
            }
        }//add end

        public void show(){
            if(this.left != null){
                this.left.show();
            }
            System.out.print(this.value + " ");

            if(this.right != null){
                this.right.show();
            }
        }

        public void delete(int num){
            Node father = this;
            Node child = null;
        }

        public Node findNode(int num){
            Node n = null;

            if(this.value == num){
                return this;
            }

            else if(num < this.value && this.left != null){
                return this.left.findNode(num);
            }

            else if(num >= this.value && this.right != null){
                return this.right.findNode(num);
            }
            else{
                return null;
            }
        }

        public Node findParentNode(int num){
            Node n = null;

            if(this.left != null && this.left.value == num){ //num 是左孩子
                return this;
            }

            else if(this.right != null && this.right.value == num){ //num 是右孩子
                return this;
            }

            else if(num < this.value && this.left != null){// num不是孩子节点,且比当前节点小,继续向左寻找
                    return this.left.findParentNode(num);
            }

            else if(num >= this.value && this.right != null){// num不是孩子节点,且比当前节点大,继续向右寻找
                    return this.right.findParentNode(num);
            }
            else{  //进行不下去了,说明无法发现节点,返回null
                System.out.println("父节点不存在");
                return null;
            }
        }

    }
}

 

posted @ 2019-11-14 20:35  不咬人的兔子  阅读(130)  评论(0编辑  收藏  举报