二分探索木のバイナリ検索ツリーの「高度なデータ構造へのエントリから楽しいです」

この記事では、ビデオチュートリアル「高度なデータ構造へのエントリから楽しい」のliuyuboboboから来ています

...............、二分探索木、二分探索木:図は、次のような多くの翻訳があるため、漢字の数が多いため、バイナリ検索ツリーです

バイナリ検索ツリーのバイナリツリーベースが、二つ以上の特徴:

図1に示すように、ノードの比較可能性を有する二分探索木の値。

図2に示すように、その左サブツリー任意の大きさの要素、および任意の要素よりも、各ノード少ない右サブツリーより(この記事は木に重複する要素については説明していません)。

増加したバイナリ検索ツリー、取得、削除操作を実現するために、次のJavaコードを使用してください。

ノードNodeクラスの定義

private class Node {
    // 节点值
    public E e;
    // 每个节点都可能有左节点、右节点。最后一层的节点没有左右节点可以视为left、right都为null
    public Node left, right;

    public Node(E e) {
        this.e = e;
        left = null;
        right = null;
    }
}

ツリーの実装

//二分搜索树
public static class BinarySearchTree<E extends Comparable<E>> {
    private class Node {
        // 节点值,二分搜索树的值是有比较性的,所以泛型E必须是Comparable的子类
        public E e;
        // 每个节点都可能有左节点、右节点。最后一层的节点没有左右节点可以视为left、right都为null
        public Node left, right;

        public Node(E e) {
            this.e = e;
            left = null;
            right = null;
        }
    }

    // 树使用root节点作为索引,增查删都从root开始操作
    private Node root;
    // 树中节点的数量,即树的大小
    private int size;

    public BinarySearchTree() {
        root = null;
        size = 0;
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    // 在此添加增查删方法
}

最初の再帰の下では、再帰は同じタイプ、小型のアルゴリズムデータの小さな問題に実際に大きな問題、ビッグデータの分割です。

木は自然の再帰的。図ファンシーブルーボックスツリーは、木が自然な再帰的な性質を持っているので、こと、木全体の小型版です。

コードツリーノードを追加します。

        // 向二分搜索树中添加新的元素e
        public void add(E e){
            // 第一次添加的是根节点
            if(root == null){
                root = new Node(e);
                size ++;
            }else{
                add(root, e);  //递归添加元素
            }
        }

        // 向以node为根的二分搜索树中插入元素e,递归算法
        private void add(Node node, E e){
            // 不考虑相同元素的情况
            if(e.equals(node.e)){
                return;
            }else if(e.compareTo(node.e) < 0 && node.left == null){
                // 新增元素比当前节点小,且当前节点左边无节点,新元素插入左边
                node.left = new Node(e);
                size ++;
                return;
            }else if(e.compareTo(node.e) > 0 && node.right == null){
                // 新增元素比当前节点大,且当前节点右边无节点,新元素插入右边
                node.right = new Node(e);
                size ++;
                return;
            }

            // 新增元素比当前节点小,且当前节点左边有节点,递归处理当前元素左边的节点
            if(e.compareTo(node.e) < 0)
                add(node.left, e);
            else{
                //e.compareTo(node.e) > 0 的情况
                // 新增元素比当前节点大,且当前节点右边有节点,递归处理当前元素右边的节点
                add(node.right, e);
            }
        }

次のように容易に理解上記のコード新しいノードが、大きな符号量は、合理化することができます。

        public void add(E e){
            // 使用root为索引
            root = add(root, e);
        }

        public Node add(Node node, E e){
            if (node == null){
                // 第一次添加元素,root就为new Node(e)
                size++;
                return new Node(e);
            }

            if (e.compareTo(node.e) < 0){
                // 在节点左边添加节点
                node.left = add(node.left, e);
            }else if (e.compareTo(node.e) > 0){
                // 在节点右边添加节点
                node.right = add(node.right, e);
            }
            return node;
        }

トラバーサルノード

途中の図トラバーサルは、先行順走査と呼ばれています

        // 前序遍历
        public void preOrder(){
            preOrder(root);
        }

        public void preOrder(Node node){
            if (node == null){
                return;
            }
            // 打印当前元素值
            System.out.println(node.e);
            preOrder(node.left);
            preOrder(node.right);
        }

順トラバーサルでは、後順トラバーサル

        // 中序遍历结果从大到小排序
        public void inOrder(){
            inOrder(root);
        }

        public void inOrder(Node node){
            if (node == null){
                return;
            }

            inOrder(node.left);
            System.out.println(node.e);
            inOrder(node.right);
        }

        // 后续遍历,先遍历父节点的子节点,在遍历父节点
        public void postOrder(){
            postOrder(root);
        }

        public void postOrder(Node node){
            if (node == null){
                return;
            }
            postOrder(node.left);
            postOrder(node.right);
            System.out.println(node.e);
        }

次の主な機能テストを書きます

       public static void main(String[] args) {
            BinarySearchTree<Integer> bts = new BinarySearchTree<>();
            int[] nums = {28, 16, 30, 22, 13, 42, 29};
            for (int num:nums){
                bts.add(num);
            }
            System.out.println("-------前序遍历------");
            bts.preOrder();
            System.out.println("------中序遍历-------");
            bts.inOrder();
            System.out.println("------后序遍历-------");
            bts.postOrde
        }

以下を達成するために、後続の、深さ優先順に属する最初のトラバーサルのプリアンブル

反復再び28、16、30の最初のトラバーサルは、最終的には13,22,29,42を通過します。これは、キューの1以上幅優先トラバーサルを介して達成することができます。

上に示したフロー

チームへの1,28

左と右のノードが空でない場合は2,28チーム、そして、チームに28のノード(16、30)について置きます

-3,16-チーム、左右のノードが空でない場合は、16左右ノード(13、チームに22)

4,30チーム、左右のノードが空でない場合は、30左右ノード(29、チームに42)

コードは次のように実装されています。

        // 广度优先遍历
        public void levelOrder(){
            if (root == null){
                return;
            }
            Queue<Node> q = new LinkedBlockingQueue<>();
            q.add(root);
            while (!q.isEmpty()){
                Node node = q.remove();
                System.out.println(node.e);
                if (node.left != null)
                    q.add(node.left);
                if (node.right != null)
                    q.add(node.right);
            }
        }

最小の木、最大値を検索します。場合ツリーの左端ノードnode.left == NULL、最小ノードの値。node.right == NULL右端ツリーノード、ノードの最大値。

図の上記の実施形態の最小値を見つけます。同様の方法の最大値を検索し、それはほとんどの右の行を見つけることです

// 最小值元素
public E minimum(){
    if(size == 0)
        throw new IllegalArgumentException("空树");
    Node minNode = minimum(root);
    return minNode.e;
}

// 最小值节点
private Node minimum(Node node){
    if( node.left == null )
        return node;
    return minimum(node.left);
}

// 最大值元素
public E maximum(){
    if(size == 0)
        throw new IllegalArgumentException("空树");
    return maximum(root).e;
}

// 最大值节点
private Node maximum(Node node){
    if( node.right == null )
        return node;
    return maximum(node.right);
}

削除最小

        public E removeMin(){
            // 查找最小值
            E ret = minimum();
            // 删除最小节点
            root = removeMin(root);
            // 返回最小值
            return ret;
        }

        public Node removeMin(Node node){
            // node.left == null,node必然是最小节点
            if (node.left == null){
                // 先保存最小节点的right
                Node nodeRight = node.right;
                // 再把最小节点的right指向null,以便java虚拟机回收最小节点内存
                node.right = null;
                size--;
                return nodeRight;
            }
            // node.left != null,递归node.left
            node.left = removeMin(node.left);
            return node;
        }

コードだけではなく、図面を与え、最大値を削除します。ロジックの最小値を削除するには、参照してください。

        public E removeMax(){
            E ret = maximum();
            root = removeMax(root);
            return ret;
        }

        private Node removeMax(Node node){
            // node.right == null,节点必然是最大节点
            if(node.right == null){
                // 保存最大节点的right
                Node leftNode = node.left;
                node.left = null;
                size --;
                return leftNode;
            }
            // node.right != null,递归node.right
            node.right = removeMax(node.right);
            return node;
        }

次のように任意の値を削除し、状況が、より多くのです。

図1に示すように、左の子ノードがnullである、最小値が削除されます。

2は、右の子ノードがnullの場合、最大値を削除することです。

図3は、子ノードが値を持っているについて、Hibbard削除アルゴリズムを使用して値を削除します。

、削除されたノードをdであると仮定すると、両方の左子供をD、子供たちが権利を持っている、あなたはD右のサブツリーを使用することができます。Hibbard削除は、コンピュータ科学者、1962年にHibbardと呼ばれ、このアルゴリズムは、単にこれですあるいは最小値D。

        public void remove(E e){
            remove(root, e);
        }

        private Node remove(Node node, E e){
            if (node == null)
                return null;
            if (e.compareTo(node.e) < 0){
                // 递归左侧节点
                node.left = remove(node.left, e);
                return node;
            }else if (e.compareTo(node.e) > 0){
                // 递归右侧节点
                node.right = remove(node.right, e);
                return node;
            }else {
                // 删除最小值
                if (node.left == null){
                    Node rightNode = node.right;
                    node.right = null;
                    size--;
                    return rightNode;
                }
                // 删除最大值
                if (node.right == null){
                    Node leftNode = node.left;
                    node.left=null;
                    size--;
                    return leftNode;
                }

                //当前node有左右孩子,找出右树中的最小节点successor
                Node successor = minimum(node.right);
                // successor.right = 删除当前node最小值后的树
                successor.right = removeMin(node.right);
                // successor.left = 当前node节点的left
                successor.left = node.left;
                node.left = node.right = null;
                // 返回successor
                return successor;
            }
        }

すべてのコード

//二分搜索树
public class BinarySearchTree<E extends Comparable<E>> {
    private class Node {
        // 节点值
        public E e;
        // 每个节点都可能有左节点、右节点。最后一层的节点没有左右节点可以视为left、right都为null
        public Node left, right;

        public Node(E e) {
            this.e = e;
            left = null;
            right = null;
        }
    }

    // 树使用root节点作为索引,增查删都从root开始操作
    private Node root;
    // 树中节点的数量,即树的大小
    private int size;

    public BinarySearchTree() {
        root = null;
        size = 0;
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public void add(E e){
        // 使用root为索引
        root = add(root, e);
    }

    public Node add(Node node, E e){
        if (node == null){
            // 第一次添加元素,root就为new Node(e)
            size++;
            return new Node(e);
        }

        if (e.compareTo(node.e) < 0){
            // 在节点左边添加节点
            node.left = add(node.left, e);
        }else if (e.compareTo(node.e) > 0){
            // 在节点右边添加节点
            node.right = add(node.right, e);
        }
        return node;
    }

    // 前序遍历
    public void preOrder(){
        preOrder(root);
    }

    public void preOrder(Node node){
        if (node == null){
            return;
        }
        // 打印当前元素值
        System.out.println(node.e);
        preOrder(node.left);
        preOrder(node.right);
    }

    // 中序遍历,结果从小到大排序
    public void inOrder(){
        inOrder(root);
    }

    public void inOrder(Node node){
        if (node == null){
            return;
        }

        inOrder(node.left);
        System.out.println(node.e);
        inOrder(node.right);
    }

    // 后序遍历,先遍历父节点的子节点,在遍历父节点
    public void postOrder(){
        postOrder(root);
    }

    public void postOrder(Node node){
        if (node == null){
            return;
        }
        postOrder(node.left);
        postOrder(node.right);
        System.out.println(node.e);
    }

    // 广度优先遍历
    public void levelOrder(){
        if (root == null){
            return;
        }
        Queue<Node> q = new LinkedBlockingQueue<>();
        q.add(root);
        while (!q.isEmpty()){
            Node node = q.remove();
            System.out.println(node.e);
            if (node.left != null)
                q.add(node.left);
            if (node.right != null)
                q.add(node.right);
        }
    }

    // 最小值元素
    public E minimum(){
        if(size == 0)
            throw new IllegalArgumentException("空树");
        Node minNode = minimum(root);
        return minNode.e;
    }

    // 最小值节点
    private Node minimum(Node node){
        if( node.left == null )
            return node;
        return minimum(node.left);
    }

    // 最大值元素
    public E maximum(){
        if(size == 0)
            throw new IllegalArgumentException("空树");
        return maximum(root).e;
    }

    // 最大值节点
    private Node maximum(Node node){
        if( node.right == null )
            return node;
        return maximum(node.right);
    }

    public E removeMin(){
        // 查找最小值
        E ret = minimum();
        // 删除最小节点
        root = removeMin(root);
        // 返回最小值
        return ret;
    }

    public Node removeMin(Node node){
        // node.left == null,node必然是最小节点
        if (node.left == null){
            // 先保存最小节点的right
            Node nodeRight = node.right;
            // 再把最小节点的right指向null,以便java虚拟机回收最小节点内存
            node.right = null;
            size--;
            return nodeRight;
        }
        // node.left != null,递归node.left
        node.left = removeMin(node.left);
        return node;
    }

    public E removeMax(){
        E ret = maximum();
        root = removeMax(root);
        return ret;
    }

    private Node removeMax(Node node){
        // node.right == null,节点必然是最大节点
        if(node.right == null){
            // 保存最大节点的right
            Node leftNode = node.left;
            node.left = null;
            size --;
            return leftNode;
        }
        // node.right != null,递归node.right
        node.right = removeMax(node.right);
        return node;
    }

    public void remove(E e){
        remove(root, e);
    }

    private Node remove(Node node, E e){
        if (node == null)
            return null;
        if (e.compareTo(node.e) < 0){
            // 递归左侧节点
            node.left = remove(node.left, e);
            return node;
        }else if (e.compareTo(node.e) > 0){
            // 递归右侧节点
            node.right = remove(node.right, e);
            return node;
        }else {
            // 删除最小值
            if (node.left == null){
                Node rightNode = node.right;
                node.right = null;
                size--;
                return rightNode;
            }
            // 删除最大值
            if (node.right == null){
                Node leftNode = node.left;
                node.left=null;
                size--;
                return leftNode;
            }

            //当前node有左右孩子,找出右树中的最小节点successor
            Node successor = minimum(node.right);
            // successor.right = 删除当前node最小值后的树
            successor.right = removeMin(node.right);
            // successor.left = 当前node节点的left
            successor.left = node.left;
            node.left = node.right = null;
            // 返回successor
            return successor;
        }
    }

    public static void main(String[] args) {

        _5_BinarySearchTree.BinarySearchTree<Integer> bst = new _5_BinarySearchTree.BinarySearchTree<>();
        int[] nums = {28, 16, 30, 22, 13, 42, 29};
        for (int num:nums){
            bst.add(num);
        }

        System.out.println("-------前序遍历------");
        bst.preOrder();
        System.out.println("------中序遍历-------");
        bst.inOrder();
        System.out.println("------后序遍历-------");
        bst.postOrder();
        System.out.println("------广度优先遍历-------");
        bst.levelOrder();


        ArrayList<Integer> nums2 = new ArrayList<>();

        //while(!bst.isEmpty())
        //    nums2.add(bst.removeMin());
        //System.out.println(nums2);

        //while(!bst.isEmpty())
        //    nums2.add(bst.removeMax());
        //System.out.println(nums2);


        //bst.remove(16);
        //bst.preOrder();

    }

}

 

 

 

公開された51元の記事 ウォン称賛14 ビュー40000 +

おすすめ

転載: blog.csdn.net/u010606397/article/details/98464575