紙上談兵: 伸展樹 (splay tree)


作者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉載,也請保留這段聲明。謝謝! 

 

我們討論過,樹的搜索效率與樹的深度有關。二叉搜索樹的深度可能為n,這種情況下,每次搜索的復雜度為n的量級。AVL樹通過動態平衡樹的深度,單次搜索的復雜度為log(n) (以上參考紙上談兵 AVL樹)。我們下面看伸展樹(splay tree),它對於m次連續搜索操作有很好的效率。

 

伸展樹會在一次搜索后,對樹進行一些特殊的操作。這些操作的理念與AVL樹有些類似,即通過旋轉,來改變樹節點的分布,並減小樹的深度。但伸展樹並沒有AVL的平衡要求,任意節點的左右子樹可以相差任意深度。與二叉搜索樹類似,伸展樹的單次搜索也可能需要n次操作。但伸展樹可以保證,m次的連續搜索操作的復雜度為mlog(n)的量級,而不是mn量級。

 

體來說,在查詢到目標節點后,伸展樹會不斷進行下面三種操作中的一個,直到目標節點成為根節點 (注意,祖父節點是指父節點的父節點)

1. zig: 當目標節點是根節點的左子節點或右子節點時,進行一次單旋轉,將目標節點調整到根節點的位置。

zig

2. zig-zag: 當目標節點、父節點和祖父節點成"zig-zag"構型時,進行一次雙旋轉,將目標節點調整到祖父節點的位置。

zig-zag

3. zig-zig:當目標節點、父節點和祖父節點成"zig-zig"構型時,進行一次zig-zig操作,將目標節點調整到祖父節點的位置。

zig-zig

單旋轉操作和雙旋轉操作見AVL樹。下面是zig-zig操作的示意圖:

zig-zig operation

在伸展樹中,zig-zig操作(基本上)取代了AVL樹中的單旋轉。通常來說,如果上面的樹是失衡的,那么A、B子樹很可能深度比較大。相對於單旋轉(想一下單旋轉的效果),zig-zig可以將A、B子樹放在比較高的位置,從而減小樹總的深度。

 

下面我們用一個具體的例子示范。我們將從樹中搜索節點2:

Original

zig-zag (double rotation)

zig-zig

zig (single rotation at root)

上面的第一次查詢需要n次操作。然而經過一次查詢后,2節點成為了根節點,樹的深度大減小。整體上看,樹的大部分節點深度都減小。此后對各個節點的查詢將更有效率。

伸展樹的另一個好處是將最近搜索的節點放在最容易搜索的根節點的位置。在許多應用環境中,比如網絡應用中,某些固定內容會被大量重復訪問(比如江南style的MV)。伸展樹可以讓這種重復搜索以很高的效率完成。

 

伸展樹的C實現

/* By Vamei */
/* Splay Tree */ #include <stdio.h> #include <stdlib.h> typedef struct node *position; typedef int ElementTP; struct node { position parent; ElementTP element; position lchild; position rchild; }; /* pointer => root node of the tree */ typedef struct node *TREE; TREE find_value(TREE, ElementTP); position insert_value(TREE, ElementTP); static void splay_tree(TREE, position); static position search_value(TREE, ElementTP); static void with_grandpa(TREE, position); static void insert_node_to_nonempty_tree(TREE, position); static TREE left_single_rotate(TREE); static TREE left_double_rotate(TREE); static TREE right_single_rotate(TREE); static TREE right_double_rotate(TREE); static TREE left_zig_zig(TREE); static TREE right_zig_zig(TREE); void main(void) { TREE tr; tr = NULL; tr = insert_value(tr, 6); tr = insert_value(tr, 5); tr = insert_value(tr, 4); tr = insert_value(tr, 3); tr = insert_value(tr, 1); tr = insert_value(tr, 2); tr = find_value(tr, 2); printf("%d\n", tr->rchild->lchild->element); } /* * insert a value into the tree * return root address of the tree */ position insert_value(TREE tr, ElementTP value) { position np; /* prepare the node */ np = (position) malloc(sizeof(struct node)); np->element = value; np->parent  = NULL; np->lchild  = NULL; np->rchild  = NULL; if (tr == NULL) tr = np; else { insert_node_to_nonempty_tree(tr, np); } return tr; } /* * * return NUll if not found */ TREE find_value(TREE tr, ElementTP value) { position np; np = search_value(tr, value); if (np != NULL && np != tr) { splay_tree(tr, np); } return np; } /* * splaying the tree after search */
static void splay_tree(TREE tr, position np) { while (tr->lchild != np && tr->rchild != np) { with_grandpa(tr, np); } if (tr->lchild == np) { right_single_rotate(tr); } else if (tr->rchild == np) { left_single_rotate(tr); } } /* * dealing cases with grandparent node */
static void with_grandpa(TREE tr, position np) { position parent, grandPa; int i,j; parent = np->parent; grandPa = parent->parent; i = (grandPa->lchild == parent) ? -1 : 1; j = (parent->lchild == np) ? -1 : 1; if (i == -1 && j == 1) { right_double_rotate(grandPa); } else if (i == 1 && j == -1) { left_double_rotate(grandPa); } else if (i == -1 && j == -1) { right_zig_zig(grandPa); } else { left_zig_zig(grandPa); } } /* * search for value */
static position search_value(TREE tr, ElementTP value) { if (tr == NULL) return NULL; if (tr->element == value) { return tr; } else if (value < tr->element) { return search_value(tr->lchild, value); } else { return search_value(tr->rchild, value); } } /* * left single rotation * return the new root */
static TREE left_single_rotate(TREE tr) { TREE newRoot, parent; parent = tr->parent; newRoot = tr->rchild; /* detach & attach */ 
    if (newRoot->lchild != NULL) newRoot->lchild->parent = tr; tr->rchild = newRoot->lchild; /* raise new root node */ newRoot->lchild = tr; newRoot->parent = parent; if (parent != NULL) { if (parent->lchild == tr) { parent->lchild = newRoot; } else { parent->rchild = newRoot; } } tr->parent = newRoot; return newRoot; } /* * right single rotation * return the new root */
static TREE right_single_rotate(TREE tr) { TREE newRoot, parent; parent = tr->parent; newRoot = tr->lchild; /* detach & attach */
    if (newRoot->rchild != NULL) newRoot->rchild->parent = tr; tr->lchild = newRoot->rchild; /* raise new root node */ newRoot->rchild = tr; newRoot->parent = parent; if (parent != NULL) { if (parent->lchild == tr) { parent->lchild = newRoot; } else { parent->rchild = newRoot; } } tr->parent = newRoot; return newRoot; } /* * left double rotation * return */
static TREE left_double_rotate(TREE tr) { right_single_rotate(tr->rchild); return left_single_rotate(tr); } /* * right double rotation * return */
static TREE right_double_rotate(TREE tr) { left_single_rotate(tr->lchild); return right_single_rotate(tr); } /* * insert a node to a non-empty tree * called by insert_value() */
static void insert_node_to_nonempty_tree(TREE tr, position np) { /* insert the node */
    if(np->element <= tr->element) { if (tr->lchild == NULL) { /* then tr->lchild is the proper place */ tr->lchild = np; np->parent = tr; return; } else { insert_node_to_nonempty_tree(tr->lchild, np); } } else if(np->element > tr->element) { if (tr->rchild == NULL) { tr->rchild = np; np->parent = tr; return; } else { insert_node_to_nonempty_tree(tr->rchild, np); } } } 
/*
* right zig-zig operation
*/
static TREE right_zig_zig(TREE tr) { position parent,middle,newRoot; parent = tr->parent; middle = tr->lchild; newRoot = tr->lchild->lchild; tr->lchild = middle->rchild; if (middle->rchild != NULL) middle->rchild->parent = tr; middle->rchild = tr; tr->parent = middle; middle->lchild = newRoot->rchild; if (newRoot->rchild != NULL) newRoot->rchild->parent = middle; newRoot->rchild = middle; middle->parent = newRoot; newRoot->parent = parent; if (parent != NULL) { if (parent->lchild == tr) { parent->lchild = newRoot; } else { parent->rchild = newRoot; } } return newRoot; }
/*
* left zig-zig operation
*/
static TREE left_zig_zig(TREE tr) { position parent,middle,newRoot; parent = tr->parent; middle = tr->rchild; newRoot = tr->rchild->rchild; tr->rchild = middle->lchild; if (middle->lchild != NULL) middle->lchild->parent = tr; middle->lchild = tr; tr->parent = middle; middle->rchild = newRoot->lchild; if (newRoot->lchild != NULL) newRoot->lchild->parent = middle; newRoot->lchild = middle; middle->parent = newRoot; newRoot->parent = parent; if (parent != NULL) { if (parent->rchild == tr) { parent->rchild = newRoot; } else { parent->lchild = newRoot; } } return newRoot; }

運行結果:

4

 

總結

splay tree, m operations: mlog(n)

zig-zig

 

歡迎繼續閱讀“紙上談兵: 算法與數據結構”系列。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM