二叉查找樹--BinarySearchTree

一、定義Tree接口
public interface Tree<E> extends Iterable<E> {
    boolean search(E e);
    boolean insert(E e);
    boolean delete(E e);
    void inorder(); //中序遍歷
    void preorder();//前序遍歷
    void postorder();//後序遍歷
    int getSize();
    boolean isEmpty();
    void clear();
}

二、實現抽象便利類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;
    }
}

三、實現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; //不能插入相同元素
            }

            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; //元素不在二叉樹中

        if(current.leftNode == null){      //如果要刪除的節點沒有左子樹
            if (parent == root)
                root = current.rightNode;
            else {
                if (e.compareTo(parent.element) > 0)
                    parent.rightNode = current.rightNode;
                else
                    parent.leftNode = current.rightNode;
            }
        }
        else{                                       //如果要刪除的節點有左子樹,
            TreeNode<E> parentOfRightMost = current;
            TreeNode<E> rightMost = current.leftNode;

            while (rightMost.rightNode != null){    //找出出左子樹中最右的子節點,即左子樹中最大的子節點rightMost
                parentOfRightMost = rightMost;
                rightMost = rightMost.rightNode;
            }
            current.element = rightMost.element;    //把左子樹中最大的節點替換掉要刪掉的節點

            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;
    }

}

四、總結

        1.前序、中序、後序遍歷的時間複雜度都是O(n),因爲每個節點都要遍歷一次。查找、刪除和插入的時間複雜度是樹的高度。在最差的情況下,樹的高度爲O(n)但如果是平衡二叉樹,高度則是O(logn)

        2.如果是平衡二叉樹,樹的大部分操作需要從上至下一層層的查找樹的節點,對於一棵滿樹,大約有一半的節點處於最底層(最底層節點數 = 其它層節點數的和 + 1),故節點操作大約有一半需要找到最底層節點,大約有四分之一的節點處於倒數第二層,故節點操作大約有四分之一需要找到倒數第二層節點,依此類推

查找過程中,需要訪問每一層的節點,故只要知道了查找的層數,就能知道操作所需的時間,如果節點總數爲N,層數爲L,L=log2(N+1)

如果爲查找操作或刪除操作,被操作的節點可能是是樹的任意節點,故查找操作或刪除操作的時間複雜度爲:1/21*log2(N+1) + 1/22*log2(N/2+1) + ... + 1/2N*1

如果爲插入操作,由於每次都在樹的最低層插入新的節點,故插入操作的時間複雜度爲:log2(N+1)

總的來說可以認爲二叉搜索樹操作的時間複雜度爲爲O(logN)

如果樹不是一棵滿樹,則判斷起來比較複雜,但是如果層數相同,對滿樹的操作肯定比對不滿樹的操作更耗時

對於一個含有10000個數據項的有序鏈表,查找操作平均需要比較5000次,對於一個含有10000個節點的二叉搜索樹,查找操作大約需要13次

對於一個含有10000個數據項的有序數組,插入操作平均需要移動5000次(對於比較次數,使用不同的算法比較次數並不相同),對於一個含有10000個節點的二叉搜索樹,插入操作只需大約13次比較就可找到待插入節點的插入位置,並且由於該位置總是處於二叉搜索樹的最底層,並不需要移動其它的節點

可以看出,二叉搜索樹集合了有序鏈表插入刪除效率高和有序數組查詢效率高的優點


發佈了14 篇原創文章 · 獲贊 4 · 訪問量 9952
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章