1. 程式人生 > >資料結構與演算法---樹

資料結構與演算法---樹

對於大量的輸入資料,連結串列的線性訪問時間太慢,不宜使用。而樹是一種簡單的資料結構,其大部分操作的執行時間平均為O(log N)

二叉樹

二叉樹是一棵樹,其中每個節點都不能有多於兩個的兒子。
實現

class BinaryNode(){
    Object element;
    BinaryNode left;
    BinaryNode right;
}

查詢樹ADT — 二叉查詢樹

使二叉樹成為二叉查詢樹的性質是,對於樹中的每個節點X,他的左子樹中所有項的值小於X中的項,而它的右子樹中所有項的值小於X中的項

public class BinarySearchTree <E extends Comparable<? super E>>{

            private static class BinaryNode<E>{
                BinaryNode(E theElement) {
                    this(theElement,null,null);
                }

                BinaryNode(E element, BinaryNode<E> left, BinaryNode<E> right) {
                    this.element = element;
                    this.left = left;
                    this.right = right;
                }

                E element;
                BinaryNode<E> left;
                BinaryNode<E> right;
            }

            private BinaryNode<E> root;

            public BinarySearchTree(){root = null;}

            public void makeEmpty(){root = null;}

            public boolean isEmpty(){return root == null;}

            public boolean contains(E x){
                return contains(x, root);
            }

            public E findMin(){
                if(isEmpty()) throw new BufferUnderflowException();
                return findMin(root).element;
            }

            public E findMax(){
                if(isEmpty()) throw new BufferUnderflowException();
                return findMax(root).element;
            }

            public void insert(E x){root = insert(x, root);}

            public void remove(E x){root = remove(x, root);}

            public void printTree(){};

            private BinaryNode<E> remove(E x, BinaryNode<E> root) {
                if (root == null) return root;
                int compareResult = x.compareTo(root.element);
                if (compareResult < 0) root.left = remove(x, root.left);
                else if (compareResult > 0) root.right = remove(x, root.right);
                else if (root.left != null && root.right != null) {
                    root.element = findMin(root.right).element;
                    root.right = remove(root.element,root.right);
                }
                else root = (root.left != null)?root.left:root.right;
                return root;
            }

            private BinaryNode<E> insert(E x, BinaryNode<E> root) {
                if (root == null) return new BinaryNode<E>(x, null, null);
                int compareResult = x.compareTo(root.element);

                if (compareResult < 0) root.left = insert(x, root.left);
                else if (compareResult > 0) root.right = insert(x, root.right);
                else ;
                return root;
            }

            private BinaryNode<E> findMax(BinaryNode<E> root) {
               if(root != null)
                   while (root.right != null)
                       root = root.right;
                return root;
            }

            private BinaryNode<E> findMin(BinaryNode<E> root) {
                if (root ==null) return null;
                else if (root.left ==null) return root;
                return  findMin(root.left);
            }

            private boolean contains(E x, BinaryNode<E> root) {
                if(root == null) return false;
                int compareResult = x.compareTo(root.element);
                if(compareResult < 0) return contains(x, root.left);
                else if(compareResult > 0) return contains(x, root.right);
                else return true;
            }

AVL樹

AVL樹是帶有平衡條件的二叉查詢樹。這個平衡條件必須要容易保持。空樹的高度定義為 -1 。每一個幾點保留高度資訊。
在插入資料時會破壞的平衡結構,所以每次插入刪除操作都需要對樹進行修正。修正的方法很簡單,我們這個操作為旋轉。
分別為單旋轉和雙旋轉,具體實現可以上網查閱資料。

public class AvlTree<E extends Comparable<? super E> >{

    private static class AvlNode<E>{

        AvlNode(E element) {
            this(element, null, null);
        }

        AvlNode(E element, AvlNode<E> left, AvlNode<E> right) {
            this.element = element;
            this.left = left;
            this.right = right;
        }

        E element;
        AvlNode<E> left;
        AvlNode<E> right;
        int height;
    }

    private AvlNode<E> root;
    public void insert(E x){root = insert(x,root);}
    private int height(AvlNode<E> root) {
        return root == null ?  -1 : root.height;
    }


    private AvlNode<E> insert(E x, AvlNode<E> t){
        System.out.println("插入"+ x);

        if(t == null) {
            System.out.println("插入根節點"+x);
            return new AvlNode<E>(x, null, null);
        }

        int compareResult = x.compareTo(t.element);
        if(compareResult < 0){
            System.out.println("插入比根節點小的值");
            t.left = insert(x,t.left);
            if(height(t.left) - height(t.right) == 2)
                if (x.compareTo(t.left.element) < 0) {
                    System.out.println("插入比根節點小的值,單旋");
                    t = rotateWithLeftChild(t);
                }
                else{
                    System.out.println("插入比根節點小的值,雙旋");
                    t = doubleWithLeftChild(t);}
        }
        else if (compareResult > 0){
            System.out.println("插入比根節點大的值");
            t.right = insert(x, t.right);
            if(height(t.right) - height(t.left) == 2)
                if(x.compareTo(t.right.element) > 0){
                    System.out.println("插入比根節點大的值,單旋");
                    t = rotateWithRightChild(t);}
                else{
                    System.out.println("插入比根節點大的值,雙旋");
                    t = doubleWithRightChild(t);}
        }
        else ;
        t.height = Math.max(height(t.left),height(t.right)) + 1;
        return t;

    }
    private AvlNode<E> rotateWithRightChild(AvlNode<E> k2) {
        AvlNode<E> k1 = k2.right;
        k2.right = k1.left;
        k1.left = k2;
        k2.height = Math.max(height(k2.right),height(k2.right))+1;
        k1.height = Math.max(height(k1.right),k2.height)+1;
        return k1;
    }
    private AvlNode<E> doubleWithRightChild(AvlNode<E> k3) {
        k3.right = rotateWithLeftChild(k3.right);
        return rotateWithRightChild(k3);
    }



    private AvlNode<E> rotateWithLeftChild(AvlNode<E> k2) {
        AvlNode<E> k1 = k2.left;
        k2.left = k1.right;
        k1.right = k2;
        k2.height = Math.max(height(k2.right),height(k2.right))+1;;
        k1.height = Math.max(height(k1.left),k2.height)+1;

        return k1;
    }

    private AvlNode<E> doubleWithLeftChild(AvlNode<E> k3) {
        k3.left = rotateWithRightChild(k3.left);
        return rotateWithLeftChild(k3);
    }



}

伸展樹

伸展樹的基本想法是,當一個節點被訪問後,它就要經過一系列AVL樹的旋轉被推到根上。如果一個節點很深,那麼其路徑上就存在許多也相對較深的節點,通過重新構造可以減少對所有這些節點很深,那麼在其路徑上就存在許多也相對較深的節點,通過重新構造可以減少對所有這些節點的進一步訪問所花費的時間。因此,那我們要求重新構造應具有平衡這棵樹的作用。

樹的遍歷

樹的遍歷方式有三種
- 先序遍歷
當前節點在其兒子節點之前處理
- 中序遍歷
先處理左子樹,然後是當前節點,再處理右子樹,其總的執行時間是O(N)
- 後序遍歷
當前節點在其兒子節點之後處理,比如計算樹的高度時
- 層序遍歷
所有深度為d的節點要在深度d+1之前進行處理,與上述三種遍歷方式不同的是它不使用遞迴方式進行,他用到佇列

B樹