Binary Search Tree--BinarySearchTree

First, define the Tree interface
public interface Tree<E> extends Iterable<E> {
    boolean search (E e);
    boolean insert(E e);
    boolean delete (E e);
    void inorder(); //Inorder traversal
    void preorder();//Preorder traversal
    void postorder();//Postorder traversal
    int getSize();
    boolean isEmpty();
    void clear();
}

2. Implement the abstract convenience class AbstractTree

public abstract class AbstractTree<E> implements Tree<E> {
    @Override
    public void inorder() {

    }

    @Override
    public void preorder() {

    }

    @Override
    public void postorder() {

    }

    @Override
    public boolean isEmpty() {
        return getSize()==0;
    }
}

3. Realize BST

public class BinarySearchTree<E extends Comparable<E>> extends AbstractTree<E>{
    protected TreeNode<E> root;
    protected int size = 0;

    public BinarySearchTree(){}
    public BinarySearchTree(E[] objects){
        for(E object:objects){
            insert(object);
        }
    }
    public TreeNode<E> getRoot() {
        return root;
    }

    public static class TreeNode<E extends Comparable<E>>{
        protected E element;
        protected TreeNode<E> leftNode;
        protected TreeNode<E> rightNode;

        public TreeNode(E element) {
            this.element = element;
        }
    }

    protected TreeNode<E> createNewNode(E e){
        return new TreeNode<>(e);
    }

    @Override
    public boolean search(E e) {
        TreeNode<E> current = root;
        while (current != null){
            if(e.compareTo(current.element) > 0){
                current = current.rightNode;
            }
            else if(e.compareTo(current.element) < 0){
                current = current.leftNode;
            }
            else
                return true;
        }
        return false;
    }

    @Override
    public boolean insert(E e) {
        if (root == null)
            root = createNewNode(e);
        else {
            TreeNode<E> parent = root;
            TreeNode<E> current = root;
            while (current != null){
                if (e.compareTo(current.element) > 0){
                    parent = current;
                    current = current.rightNode;
                }
                else if(e.compareTo(current.element) < 0){
                    parent = current;
                    current = current.leftNode;
                }
                else
                    return false; // cannot insert the same element
            }

            if (e.compareTo(parent.element) > 0){
                parent.rightNode = createNewNode(e);
            }
            else{
                parent.leftNode = createNewNode(e);
            }
        }
        size++;
        return true;
    }

    @Override
    public boolean delete (E e) {
        TreeNode<E> parent = null;
        TreeNode<E> current = root;

        while(current!=null){
            if (e.compareTo(current.element)>0){
                parent = current;
                current = current.rightNode;
            }
            else if (e.compareTo(current.element)<0){
                parent = current;
                current = current.leftNode;
            }
            else break;
        }

        if (current == null) return false; //The element is not in the binary tree

        if(current.leftNode == null){ //If the node to be deleted has no left subtree
            if (parent == root)
                root = current.rightNode;
            else {
                if (e.compareTo(parent.element) > 0)
                    parent.rightNode = current.rightNode;
                else
                    parent.leftNode = current.rightNode;
            }
        }
        else{ //If the node to be deleted has a left subtree,
            TreeNode<E> parentOfRightMost = current;
            TreeNode<E> rightMost = current.leftNode;

            while (rightMost.rightNode != null){ //Find out the rightmost child node in the left subtree, that is, the largest child node rightMost in the left subtree
                parentOfRightMost = rightMost;
                rightMost = rightMost.rightNode;
            }
            current.element = rightMost.element; //Replace the largest node in the left subtree with the node to be deleted

            if (parentOfRightMost == current)
                parentOfRightMost.leftNode = rightMost.leftNode;
            else
                parentOfRightMost.rightNode = rightMost.leftNode;
        }
        size--;
        return true;
    }

    @Override
    public void inorder() {
        inorder(root);
    }
    protected void inorder(TreeNode<E> root){
        if (root == null) return;
        inorder(root.leftNode);
        System.out.print(root.element+" ");
        inorder(root.rightNode);
    }
    @Override
    public void preorder() {
        preorder(root);
    }
    protected void preorder(TreeNode<E> root){
        if (root == null) return;
        System.out.print(root.element+" ");
        preorder(root.leftNode);
        preorder(root.rightNode);
    }

    @Override
    public void postorder() {
        postorder (root);
    }
    protected void postorder(TreeNode<E> root){
        if (root == null) return;
        postorder(root.leftNode);
        postorder(root.rightNode);
        System.out.print(root.element+" ");
    }
    @Override
    public int getSize() {
        return size;
    }

    @Override
    public void clear() {
        root = null;
        size = 0;
    }


    private class InorderIterator implements Iterator<E>{
        private ArrayList<E> list = new ArrayList<>();
        private int current = 0;
        public InorderIterator(){
            inorder();
        }
        private void inorder(){
            inorder(root);
        }
        private void inorder(TreeNode<E> root){
            if (root == null) return;
            inorder(root.leftNode);
            list.add(root.element);
            inorder(root.rightNode);
        }

        @Override
        public void remove() {
            delete(list.get(current));
            list.clear();
            inorder();
        }

        @Override
        public boolean hasNext() {
            return current<list.size();
        }

        @Override
        public E next() {
            return list.get(current++);
        }
    }

    @Override
    public Iterator iterator() {
        return new InorderIterator();
    }

    public ArrayList<TreeNode<E>> getPath(E e){
        ArrayList<TreeNode<E>> list = new ArrayList<>();
        TreeNode<E> current = root;
        while (current != null){
            list.add(current);
            if(e.compareTo(current.element)>0)
                current = current.rightNode;
            else if (e.compareTo(current.element)<0)
                current = current.leftNode;
            else
                break;
        }
        return list;
    }

}

4. Summary

        1. The time complexity of pre-order, in-order, and post-order traversal is O(n), because each node has to be traversed once. The time complexity of lookups, deletions and insertions is the height of the tree. In the worst case, the height of the tree is O(n) . But if it is a balanced binary tree, the height is O(logn) .

        2. If it is a balanced binary tree, most operations of the tree need to search the nodes of the tree from top to bottom. For a full tree, about half of the nodes are at the bottom (the number of nodes at the bottom = the number of nodes in other layers). Sum + 1), so about half of the node operations need to find the bottommost node, about a quarter of the nodes are in the penultimate layer, so about a quarter of the node operations need to find the penultimate layer node, according to And so on

During the search process, it is necessary to visit the nodes of each layer, so as long as you know the number of layers to be searched, you can know the time required for the operation. If the total number of nodes is N and the number of layers is L, L=log 2 (N+1)

If it is a search operation or a delete operation, the operated node may be any node of the tree, so the time complexity of the search operation or delete operation is: 1/2 1 *log 2 (N+1) + 1/2 2 * log 2 (N/2+1) + ... + 1/2 N *1

If it is an insertion operation, since a new node is inserted at the lowest level of the tree every time, the time complexity of the insertion operation is: log 2 (N+1)

In general, it can be considered that the time complexity of binary search tree operations is O(logN)

If the tree is not a full tree, it is more complicated to judge, but if the number of layers is the same, the operation on the full tree is definitely more time-consuming than the operation on the unsatisfactory tree.

For an ordered linked list with 10,000 data items , the lookup operation requires an average of 5,000 comparisons, and for a binary search tree with 10,000 nodes, the lookup operation takes about 13 times.

For an ordered array with 10,000 data items , the insertion operation requires an average of 5,000 moves (for the number of comparisons, the number of comparisons varies with different algorithms). For a binary search tree with 10,000 nodes, the insertion operation only requires It takes about 13 comparisons to find the insertion position of the node to be inserted, and since the position is always at the bottom of the binary search tree, there is no need to move other nodes

It can be seen that the binary search tree combines the advantages of high insertion and deletion efficiency of ordered linked list and high efficiency of ordered array query.


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325641104&siteId=291194637