伸展樹的旋轉
zig-zig旋轉(一字型旋轉)
zig-zag旋轉(之字形旋轉)
所有的旋轉無外乎這兩種,但是最大的區別就是Z比Y是大還是小,也就是書上會說的要查詢的節點Z需要比較其父結點P(z)以及祖父結點G(z),如果比同時比P(z)和G(z)大(小)的話就是zig-zig旋轉,如果比P(z)和G(z)一個大一個小的話就是zig-zag旋轉
掛到L樹
- 掛到L樹下的結點都是比要查詢的Z結點小的,先掛上去的是最小的,後掛上去的比先掛上去的大
- 因為後掛到L上的結點比先掛上去的大,所以插入位置應該是在L最大結點的右結點上
- 第一次就掛到L的右孩子的位置上
- 第二次就掛到L最大結點的右孩子上,以此類推
掛到R樹
- 掛到R樹下的結點都是比要查詢的Z結點大的,後掛上去的也是比先掛上去的小
- 第一次掛到R的左孩子位置上
- 第二次掛到R上的時候,新移動過去的2-3個結點組成的樹的根結點一定是R的左孩子,需要一次zig-zig旋轉
連線
- 需要訪問結點的左子樹掛到L的最大結點的右孩子位置上
- 需要訪問結點的右子樹掛到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!!!)