伸展樹Java實現

_Git發表於2017-12-21

特性

伸展樹(Splay Tree)是特殊的二叉查詢樹,當某個節點被訪問時,伸展樹會通過旋轉使該節點成為樹根。

樹結點和建構函式

public class SplayTree<T extends Comparable<T>> {

    private class SplayTreeNode<T extends Comparable<T>> {
        public T key;
        public SplayTreeNode<T> left;
        public SplayTreeNode<T> right;

        public SplayTreeNode() {
            this.left = null;
            this.right = null;
        }

        public SplayTreeNode(T key, SplayTreeNode<T> left, SplayTreeNode<T> right) {
            this.key = key;
            this.left = left;
            this.right = right;
        }
    }
	// 根結點
    private SplayTreeNode<T> mRoot; 
}
複製程式碼

旋轉

  1. 首選做一個迴圈,直到tree.left或者tree.right==null為止
  2. 當需要訪問的key比每次迴圈的根結點的值小的時候,就需要右旋轉;比根結點大的時候就需要左旋轉。
  3. 左旋和右旋的方式和AVL樹種LL和RR的方式一樣,只是最後多一個賦值成新tree的操作。
  4. 連結的過程,如果是右旋的話(LL方式旋轉)則設定一個N的空結點,讓原來這顆樹作為N的左孩子;如果是左旋的話(RR方式旋轉)則設定一個N的空結點,讓原來這棵樹成為N的右孩子
 private SplayTreeNode<T> splay(SplayTreeNode<T> tree, T key) {
        if (tree == null)
            return tree;

        SplayTreeNode<T> N = new SplayTreeNode<>();
        SplayTreeNode<T> l = N;
        SplayTreeNode<T> r = N;
        SplayTreeNode<T> c;
        for (; ; ) {
            int cmp = key.compareTo(tree.key);
            if (cmp < 0) {
                //在左子樹上
                if (tree.left == null)
                    break;
                if (key.compareTo(tree.left.key) < 0) {
                    c = tree.left;
                    tree.left = c.right;
                    c.right = tree;
                    tree = c;
                    if (tree.left == null)
                        break;
                }
                //這裡是link right
                r.left = tree;
                r = tree;
                tree = tree.left;
            } else if (cmp > 0) {
                if (tree.right == null)
                    break;
                if (key.compareTo(tree.right.key) > 0) {
                    //這裡是一個類似AVL樹RR旋轉的過程
                    c = tree.right;
                    tree.right = c.left;
                    c.left = tree;
                    //這裡就完成了一次旋轉,形成新的tree
                    tree = c;
                    //判斷tree是否有右結點,沒有的話就旋轉結束了
                    if (tree.right == null)
                        break;
                }

                //這裡是一個link left的過程,在最上的結點上新增一個空結點,讓這個空結點成為tree,
                l.right = tree;
                l = tree;
                tree = tree.right;
            } else
                break;
        }
        //這裡是組合的過程,這裡不懂
        l.right = tree.left;
        r.left = tree.right;
        tree.left = N.right;
        tree.right = N.left;
        return tree;
    }
複製程式碼

插入結點

 private SplayTreeNode<T> insert(SplayTreeNode<T> tree, SplayTreeNode<T> z) {
        int cmp;
        SplayTreeNode<T> y = null;
        SplayTreeNode<T> x = tree;
        while (x != null) {
            y = x;
            cmp = z.key.compareTo(x.key);
            if (cmp < 0) {
                //在左子樹上;
                x = x.left;
            } else if (cmp > 0) {
                //在右子樹上
                x = x.right;
            } else {
                z = null;
                return tree;
            }
        }

        if (y== null)
            tree = z;
        else {
            cmp = z.key.compareTo(y.key);
            if (cmp < 0)
                y.left = z;
            else
                y.right = z;
        }

        return tree;

    }
複製程式碼

刪除結點


 /*
     * 刪除結點(z),並返回被刪除的結點
     *
     * 引數說明:
     *     bst 伸展樹
     *     z 刪除的結點
     */
    private SplayTreeNode<T> remove(SplayTreeNode<T> tree, T key) {
        SplayTreeNode<T> x;

        if (tree == null)
            return null;

        // 查詢鍵值為key的節點,找不到的話直接返回。
        if (search(tree, key) == null)
            return tree;

        // 將key對應的節點旋轉為根節點。
        tree = splay(tree, key);

        if (tree.left != null) {
            // 將"tree的前驅節點"旋轉為根節點
            x = splay(tree.left, key);
            // 移除tree節點
            x.right = tree.right;
        } else
            x = tree.right;

        tree = null;

        return x;
    }

    public void remove(T key) {
        mRoot = remove(mRoot, key);
    }
複製程式碼

Reference

伸展樹 自底向上 自頂向下 Java資料結構與演算法解析(八)——伸展樹

相關文章