AVL樹(自平衡的二叉搜索樹)

一、談二叉搜索樹


增加的最壞時間複雜度 刪除的最壞時間複雜度 查詢的最壞時間複雜度
鏈表 O(N) + O(1) O(N) + O(1) O(N)
二叉搜索樹 O(N) + O(1) O(N) + O(1) O(N)

       對於二叉搜索樹,如果添加數據的時候數據隨機分佈的話,插入結點的時間複雜度會是O(h)、O(logN)。但是考慮下面兩種極端情況,當**數據從小到大或者從大到小插入**的時候,裂開,複雜度直接變爲O(N),而且**二叉搜索樹變得和普通鏈表**一樣了。

AltAlt

       不但插入結點會涼涼,刪除結點可能也會變成鏈表,考慮下面情況:

Alt

       其實出現上述情況的根源還是二叉搜索樹失衡了




二、AVL樹

1、AVL樹(自平衡)特點

       每個結點的平衡因子的絕對值小於等於1,一旦超過就是失衡,balanceFactor計算方法:平衡因子 = 左子樹高度 - 右子樹高度

       AVL樹增加刪除查詢結點的時間複雜度都是O(h)、O(logN)。



2、設計結構圖:

Alt


3、注意點

寫代碼要搞清楚以下兩個點:

① 什麼時候會出現失衡?
二叉搜索樹插入結點和刪除結點的時候會出現失衡情況。其實插入結點和刪除結點也是不一樣的,插入結點造成的失衡,只要往上找到第一個失衡的祖父結點,之後旋轉同時將該結點及子結點的高度更新後,祖父結點之上失衡的結點也會變得平衡。然後刪除結點恰恰相反,可能牽一髮而動全身,不過最壞情況也不過是執行O(logN)次旋轉操作。

② 怎麼解決失衡?四種旋轉情況對號入座

4、四種旋轉情況(g、p、n三個結點)分析

       第一種情況:LL造成的失衡,g結點右旋轉。
       第二種情況:RR造成的失衡,g結點左旋轉。
       第三種情況:LR造成的失衡,先將p結點左旋轉,再將g結點右旋轉。
       第四種情況:RL造成的失衡,先將p結點右旋轉,再將g結點左旋轉。

       旋轉情況比較簡單,主要還是確定三個點,這三個點都是結點左右子樹中比較長的結點。其中三個點用來判斷是哪一種旋轉情況,而 g 和 p 結點是專門用來旋轉的。


       模板設計模式搭好AVL樹插入結點和刪除結點的算法框架,在之前BST插入代碼裏面寫好接口,AVL樹實現相應解決失衡的方法。



5、代碼實現

	public class BinarySearchTree<E> extends BinaryTree<E> {
	
	    private Comparator<E> comparator;
	
	
	    public BinarySearchTree(Comparator<E> comparator) {
	        this.comparator = comparator;
	    }
	
	    public BinarySearchTree() {
	        this(null);     //調用有參構造
	    }
	
	    private int compare(E e1, E e2) {
	        if (comparator != null)
	            return comparator.compare(e1, e2);
	        else
	            return ((Comparable) e1).compareTo(e2);
	    }
	
	    public boolean contains(E element) {
	        return getNode(element) != null;
	    }
	
	    public Node<E> getNode(E element) {
	        Node<E> node = root;
	        while (node != null) {
	            int cmp = compare(element, node.element);
	            if (cmp > 0) {
	                node = node.right;
	            } else if (cmp < 0) {
	                node = node.left;
	            } else {
	                return node;
	            }
	        }
	        return null;
	    }
	
	    protected Node<E> createNode(Node<E> parent, E element) {
	        return new Node(parent, element);
	    }
	
	    /**
	     * BST插入一個結點
	     *
	     * @param element
	     */
	    public void add(E element) {
	        Node<E> node = root;
	        if (root == null) {    //插入第一個結點
	            root = createNode(null, element);
	            size++;
	
	            afterAdd(root);     //判斷祖父結點們是否失衡在子類裏進行處理
	
	            return;
	        }
	        int cmp = 0;
	        Node<E> t = null;
	        while (node != null) {
	            t = node;
	            cmp = compare(element, node.element);
	            if (cmp > 0) {          //待插入的元素比根結點元素大
	                node = node.right;
	            } else if (cmp < 0) {   //待插入的元素比根結點元素小
	                node = node.left;
	            } else {
	                return;
	            }
	        }
	        if (cmp > 0) {
	            t.right = createNode(t, element);
	            afterAdd(t.right);  //判斷祖父結點們是否失衡在子類裏進行處理
	        } else {
	            t.left = createNode(t, element);
	            afterAdd(t.left);   //判斷祖父結點們是否失衡在子類裏進行處理
	        }
	        size++;
	
	
	    }
	
	    /**
	     * 模板設計模式  交給子類來實現
	     *
	     * @param node
	     */
	    protected void afterAdd(Node<E> node) {}
	
	    /**
	     * BST刪除節點要分情況, 度爲0、1、2
	     *
	     * @param element
	     */
	    public void remove(E element) {
	        Node<E> node = getNode(element);
	        if (node == null)
	            return;
	
	        if (getDegree(node) == 0) {    //度爲0, 直接刪除即可
	            if (node.parent == null)   //只有一個根結點
	                root = null;
	            if (node == node.parent.left)
	                node.parent.left = null;
	            else
	                node.parent.right = null;
	
	            afterRemove(node);  //判斷祖父結點們是否失衡在子類裏進行處理
	
	        } else if (getDegree(node) == 1) { //度爲1, 找自己的左兒子或右兒子替代
	            if (node.left != null) {    //找左兒子代替
	                if (node.parent == null)   //只有一個根結點
	                    root = node.left;
	                if (node.parent.left == node) {
	                    node.parent.left = node.left;
	                    node.left.parent = node.parent;   //改變父結點
	                } else {
	                    node.parent.right = node.left;
	                    node.left.parent = node.parent;   //改變父結點
	                }
	
	                afterRemove(node);   //判斷祖父結點們是否失衡在子類裏進行處理
	            } else {    //找右兒子代替
	                if (node.parent == null)   //只有一個根結點
	                    root = node.right;
	                if (node.parent.left == node) {
	                    node.parent.left = node.right;
	                    node.right.parent = node.parent;   //改變父結點
	                } else {
	                    node.parent.right = node.right;
	                    node.right.parent = node.parent;   //改變父結點
	                }
	
	                afterRemove(node);   //判斷祖父結點們是否失衡在子類裏進行處理
	            }
	        } else {    //度爲2, 找前驅或者後繼, 拷貝數據然後刪除找的結點
	            Node<E> t = getSuccessor(node);
	            node.element = t.element;
	
	            int s = getDegree(t);     //前驅結點和後繼結點只可能是度爲1或者度爲0
	            if (s == 1) {  //度爲1
	                if (t.left != null) {
	                    if (t.parent.left == t) {
	                        t.parent.left = t.left;
	                        t.left.parent = t.parent;
	                    } else {
	                        t.parent.right = t.left;
	                        t.left.parent = t.parent;
	                    }
	
	                    afterRemove(t);  //判斷祖父結點們是否失衡在子類裏進行處理
	                } else {
	                    if (t.parent.left == t) {
	                        t.parent.left = t.right;
	                        t.right.parent = t.parent;
	                    } else {
	                        t.parent.right = t.right;
	                        t.right.parent = t.parent;
	                    }
	
	                    afterRemove(t);  //判斷祖父結點們是否失衡在子類裏進行處理
	                }
	            } else {    //度爲0
	                if (t.parent.left == t)
	                    t.parent.left = null;
	                else
	                    t.parent.right = null;
	
	                afterRemove(t);  //判斷祖父結點們是否失衡在子類裏進行處理
	            }
	        }
	    }
	
	    /**
	     * 模板設計模式  交給子類來實現
	     *
	     * @param node
	     */
	    protected void afterRemove(Node<E> node) {}
	    
    }
	public class AVLTree<E> extends BinarySearchTree<E> {
	
	    public AVLTree(Comparator<E> comparator) {
	        super(comparator);
	    }
	
	    public AVLTree() {
	        super();
	    }
	
	    private class AVLNode<E> extends Node<E> {
	        private int height = 1;
	
	        public int getHeight() {
	            return height;
	        }
	
	        public void setHeight(int height) {
	            this.height = height;
	        }
	
	        public AVLNode(Node parent, E element) {
	            super(parent, element);
	        }
	
	        /**
	         * 計算平衡因子
	         *
	         * @return
	         */
	        public int balanceFactor() {
	            int leftHeight = this.left == null ? 0 : ((AVLNode<E>) this.left).height;
	            int rightHeight = this.right == null ? 0 : ((AVLNode<E>) this.right).height;
	            return leftHeight - rightHeight;
	        }
	
	        /**
	         * 更新高度
	         */
	        public void updateHeight() {
	            int leftHeight = this.left == null ? 0 : ((AVLNode<E>) this.left).height;
	            int rightHeight = this.right == null ? 0 : ((AVLNode<E>) this.right).height;
	            height = 1 + Math.max(leftHeight, rightHeight);
	        }
	
	        /**
	         * 找到自己的左右子樹中長的那個
	         *
	         * @return
	         */
	        public Node getTallerChild() {
	            int leftHeight = this.left == null ? 0 : ((AVLNode<E>) this.left).height;
	            int rightHeight = this.right == null ? 0 : ((AVLNode<E>) this.right).height;
	            if (leftHeight > rightHeight)
	                return left;
	            else
	                return right;
	            // TODO 相等情況
	        }
	    }
	
	    private boolean isBalance(Node<E> node) {
	        return Math.abs(((AVLNode<E>) node).balanceFactor()) <= 1;
	    }
	
	    private void updateHeight(Node<E> node) {
	        ((AVLNode<E>) node).updateHeight();
	    }
	
	    @Override
	    protected Node<E> createNode(Node<E> parent, E element) {
	        return new AVLNode(parent, element);
	    }
	
	    /**
	     * 左旋轉
	     *
	     * @param g
	     */
	    private void rotateLeft(Node<E> g) {    //只需要動g 和 p
	        Node<E> p = g.right;
	
	        g.right = p.left;
	        p.left = g;
	
	        //p變爲根結點
	        p.parent = g.parent;
	        if (g.parent == null) {    //旋轉的是根結點
	            root = p;
	        } else if (g.parent.left == g)
	            g.parent.left = p;
	        else if (g.parent.right == g)
	            g.parent.right = p;
	
	        //改變其他結點的parent
	        if (g.right != null)
	            g.right.parent = g;
	        g.parent = p;
	
	        //更新高度
	        updateHeight(g);
	        updateHeight(p);
	    }
	
	    /**
	     * 右旋轉
	     *
	     * @param g
	     */
	    private void rotateRight(Node<E> g) {   //只需要動g 和 p
	        Node<E> p = g.left;
	
	        g.left = p.right;
	        p.right = g;
	
	        //p變爲根結點
	        p.parent = g.parent;
	        if (g.parent == null) {    //旋轉的是根結點
	            root = p;
	        } else if (g.parent.left == g)
	            g.parent.left = p;
	        else if (g.parent.right == g)
	            g.parent.right = p;
	
	        //改變其他結點的parent
	        if (g.left != null)
	            g.left.parent = g;
	        g.parent = p;
	
	        //更新高度
	        updateHeight(g);
	        updateHeight(p);
	    }
	
	    @Override
	    protected void afterAdd(Node<E> n) {
	        Node<E> grandparent = n;
	
	        while ((grandparent = grandparent.parent) != null) {
	            if (isBalance(grandparent)) {      //結點平衡直接更新高度
	                updateHeight(grandparent);
	            } else {        //結點不平衡需要旋轉操作
	                //先找到g p n 三個結點(g是第一個不平衡結點中比較長的孩子結點)
	                Node<E> parent = ((AVLNode<E>) grandparent).getTallerChild();
	                Node<E> node = ((AVLNode<E>) parent).getTallerChild();
	
	                if (grandparent.left == parent) {
	                    if (parent.left == node) {              //LL
	                        rotateRight(grandparent);
	                    } else if (parent.right == node) {      //LR
	                        rotateLeft(parent);
	                        rotateRight(grandparent);
	                    }
	                } else if (grandparent.right == parent) {
	                    if (parent.left == node) {              //RL
	                        rotateRight(parent);
	                        rotateLeft(grandparent);
	                    } else if (parent.right == node) {      //RR
	                        rotateLeft(grandparent);
	                    }
	                }
	                break;  //添加結點引起的失衡只需旋轉一次
	            }
	        }
	    }
	
	    @Override
	    protected void afterRemove(Node<E> n) {
	        Node<E> grandparent = n;
	
	        while ((grandparent = grandparent.parent) != null) {
	            if (isBalance(grandparent)) {      //結點平衡直接更新高度
	                updateHeight(grandparent);
	            } else {        //結點不平衡需要旋轉操作
	                //先找到g p n 三個結點(g是第一個不平衡結點中比較長的孩子結點)
	                Node<E> parent = ((AVLNode<E>) grandparent).getTallerChild();
	                Node<E> node = ((AVLNode<E>) parent).getTallerChild();
	
	                if (grandparent.left == parent) {
	                    if (parent.left == node) {              //LL
	                        rotateRight(grandparent);
	                    } else if (parent.right == node) {      //LR
	                        rotateLeft(parent);
	                        rotateRight(grandparent);
	                    }
	                } else if (grandparent.right == parent) {
	                    if (parent.left == node) {              //RL
	                        rotateRight(parent);
	                        rotateLeft(grandparent);
	                    } else if (parent.right == node) {      //RR
	                        rotateLeft(grandparent);
	                    }
	                }
	                //刪除結點引起的失衡可能不止一次不要直接break
	            }
	        }
	    }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章