对伸展树的伸展操作理解

伸展树的旋转

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

对伸展树的伸展操作理解

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

对伸展树的伸展操作理解

所有的旋转无外乎这两种,但是最大的区别就是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旋转

连接

对伸展树的伸展操作理解
  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!!!)