對伸展樹的伸展操作理解

_Git發表於2018-01-02

伸展樹的旋轉

zig-zig旋轉(一字型旋轉)

image

zig-zag旋轉(之字形旋轉)

image

所有的旋轉無外乎這兩種,但是最大的區別就是Z比Y是大還是小,也就是書上會說的要查詢的節點Z需要比較其父結點P(z)以及祖父結點G(z),如果比同時比P(z)和G(z)大(小)的話就是zig-zig旋轉,如果比P(z)和G(z)一個大一個小的話就是zig-zag旋轉

掛到L樹

  1. 掛到L樹下的結點都是比要查詢的Z結點小的,先掛上去的是最小的,後掛上去的比先掛上去的大
  2. 因為後掛到L上的結點比先掛上去的大,所以插入位置應該是在L最大結點的右結點上
    1. 第一次就掛到L的右孩子的位置上
    2. 第二次就掛到L最大結點的右孩子上,以此類推

掛到R樹

  1. 掛到R樹下的結點都是比要查詢的Z結點大的,後掛上去的也是比先掛上去的小
  2. 第一次掛到R的左孩子位置上
  3. 第二次掛到R上的時候,新移動過去的2-3個結點組成的樹的根結點一定是R的左孩子,需要一次zig-zig旋轉

連線

image

  1. 需要訪問結點的左子樹掛到L的最大結點的右孩子位置上
  2. 需要訪問結點的右子樹掛到R的最小結點的左孩子位置上

程式碼實現(不全對)

 private SplayNode<T> splay(SplayNode<T> tree, T key) {
        if (tree == null)
            return tree;
        SplayNode<T> R = new SplayNode<>();
        SplayNode<T> L = new SplayNode<>();
        SplayNode<T> cur;
        for (; ; ) {
            int cmp = key.compareTo(tree.key);
            if (cmp < 0) {
                if (tree.left == null)
                    break;
                cmp = key.compareTo(tree.left.key);
                //在左孫子上
                if (cmp < 0) {
                    cur = tree.left;
                    tree.left = cur.right;
                    cur.right = tree;
                    tree = cur.left;
                    cur.left = R.left;
                    R.left = cur;
                } else {
                    SplayNode<T> temp = tree.left;
                    tree.left = R.left;
                    R.left = tree;
                    tree = temp;
                }
            } else if (cmp > 0) {
                if (tree.right == null)
                    break;
                cmp = key.compareTo(tree.right.key);
                if (cmp > 0) {
                    cur = tree.right;
                    tree.right = cur.left;
                    cur.left = tree;
                    tree = cur.right;
                    //斷開和tree的聯絡,掛到L上去
                    cur.right = null;
                    SplayNode<T> leftBiggest = L;
                    while (leftBiggest.right != null)
                        leftBiggest = leftBiggest.right;
                    leftBiggest.right = cur;
                } else {
                    SplayNode<T> temp = tree.right;
                    tree.right = null;
                    SplayNode<T> leftBiggest = L;
                    while (leftBiggest.right != null)
                        leftBiggest = leftBiggest.right;
                    leftBiggest.right = tree;
                    tree = temp;
                }

            } else {
                break;
            }
        }
        SplayNode<T> A = tree.left;
        SplayNode<T> B = tree.right;
        SplayNode<T> tmp;
        tmp = L;
        while (tmp.right != null)
            tmp = tmp.right;
        tmp.right = A;
        tmp = R;
        while (tmp.left != null)
            tmp = tmp.left;
        tmp.left = B;
        tree.right = R.left;
        tree.left = L.right;
        return tree;
    }
複製程式碼

以上程式碼基本實現,對於結點不存在的查詢可能會出現崩潰~ 寫此僅用來理解

程式碼理解(網上標準)

private SplayNode<T> splay2(SplayNode<T> tree, T key) {
        if (tree == null)
            return null;
        SplayNode<T> N = new SplayNode<>();
        SplayNode<T> l = N;
        SplayNode<T> r = N;
        SplayNode<T> cur;
        int cmp;
        while (true) {
            cmp = key.compareTo(tree.key);
            if (cmp < 0) {
                if (tree.left == null)
                    break;
                if (key.compareTo(tree.left.key) < 0) {
                    cur = tree.left;
                    tree.left = cur.right;
                    cur.right = tree;
                    tree = cur;
                    if (tree.left == null)
                        break;
                }
                r.left = tree;
                r = tree;
                tree = tree.left;
            } else if (cmp > 0) {
                if (tree.right == null)
                    break;
                if (key.compareTo(tree.right.key) > 0) {
                    cur = tree.right;
                    tree.right = cur.left;
                    cur.left = tree;
                    tree = cur;
                    if (tree.right == null)
                        break;
                }
                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;
    }
複製程式碼

總結:根據前面的理解

l.right = tree.left;
r.left = tree.right;
tree.left = N.right;
tree.right = N.left;
複製程式碼

這裡就是連線的過程 整個程式碼中最重要的是兩個部分要理解

SplayNode<T> N = new SplayNode<>();
SplayNode<T> l = N;
SplayNode<T> r = N;
複製程式碼

這裡是一個引用的指向(具體可以去查詢Java中物件與引用的關係),l和r以及N都是指向同一個物件(至少初始的時候是的) 直到

l.right = tree;
l = tree;
tree = tree.right;
複製程式碼
r.left = tree;
r = tree;
tree = tree.left
複製程式碼

以其中一個分析,l.right = tree,把旋轉好的新樹掛到l的右孩子上(與此同時,r N的右孩子也都有值了!!!),l = tree,l指向tree,而這時候(N r的右孩子也都成為了l!!!)

相關文章