SplayTree 伸展樹:自調節的二叉搜尋樹,能把最近訪問的節點移到根節點出。適用於各種實際應用,尤其適合實現cache和garbage collection演算法。空間少,效能平攤也有競爭力,編程易實現。
優點:
1、 實現簡單,比AVL、紅/黑樹狀結構更簡單。
2、 效能好,平均效能比得上其他平衡二叉搜尋樹。可能會比AVL慢點。常數因子的差距。
3、 沒有其他的記錄資料。
4、 可以適用於相同鍵的資料,平攤仍然是O(lgn)。類似於穩定排序演算法。
缺點:
1、 高度可能會退化到線性,但是平攤仍然是O(lgn)。
2、 多線程的“唯讀”操作也會改變,需要額外的處理。
支援的操作:
1、訪問、動態刪除插入,其他平衡樹也可以做到。
2、拆分(子樹1小於子樹2)、合并。
支援的區間操作:
1、砍樹:把一個區間[x,y]的所有節點群組織成一個子樹。
依次訪問y+1和x-1,Splay操作需要改動,伸展到root或是root->left或是root->right。這點是下面的基本。
2、對一段區間都增加一個值value
採用線段樹的延時策略,必要時才往子節點更新。
3、翻轉一個區間(順序反轉)
4、對一個區間向右移動k步(轉化成交換兩個相鄰的區間):實際上就是一段區間向右迴圈移動:
砍出一個子樹,在子樹裡面繼續砍出子樹,交換子樹位置。
5、插入一段區間
也有Splay操作。
6、刪除一段區間
簡單,把區間砍成一棵子樹。不要忘了Splay子樹的父節點操作。
7、返回一段區間的最小值
砍樹,得到子樹根節點的統計資訊。
poj 3580 代碼:
#include <iostream>#include <cstdio>using namespace std;#define TREESIZE 2000010#define ARRAYSIZE 100010#define INTMAX (1<<30)struct Node {//int id;int value, min, delta, size;bool rev;Node * parent, *child[2];};Node nodes[TREESIZE];int curNode=0;int seq[ARRAYSIZE];Node *null, *root;Node* AllocNode( int value ){nodes[curNode].child[0] = nodes[curNode].child[1] = null;nodes[curNode].delta = 0;nodes[curNode].size = 1;nodes[curNode].rev = false;nodes[curNode].min = nodes[curNode].value = value;++ curNode;return &nodes[curNode-1];}void Update( Node* p ){// child一定存在if ( p == null )return;p->min = std::min( p->value, std::min( p->child[0]->min, p->child[1]->min ) );p->size = p->child[0]->size + p->child[1]->size + 1;}void InitTree(){//標誌不參與計數null = NULL;null = AllocNode(INTMAX);null->size = 0;//兩個哨兵,參與計數//頭結點哨兵root = AllocNode(INTMAX);null->child[0] = null->child[1] = root;root->parent = null;//尾節點哨兵root->child[1] = AllocNode(INTMAX);root->child[1]->parent = root;Update( root );}void Pushdown( Node * p ){if ( p == null )return;if ( 0 != p->delta ){p->value += p->delta;p->min += p->delta; //goodp->child[0]->delta += p->delta;p->child[1]->delta += p->delta;p->delta = 0;}if ( p->rev ){Node *l = p->child[0];p->child[0] = p->child[1];p->child[1] = l;p->rev = false;p->child[0]->rev = !p->child[0]->rev;p->child[1]->rev = !p->child[1]->rev;}}//[): beg-->end-1Node* MakeTree( int *interval, int beg, int end ){if ( beg == end )return null;if ( beg+1 == end ){Node *p = AllocNode(interval[beg]);p->min = interval[beg];return p;}int mid = ( beg + end )>>1;Node *p, *l, *r;p = AllocNode( interval[mid] );p->size = end-beg;l = MakeTree( interval, beg, mid );r = MakeTree( interval, mid+1, end );p->child[0] = l;p->child[1] = r;l->parent = r->parent = p;//updateUpdate( p );return p;}//旋轉:注意把延遲的資訊壓入子女節點中/* * Select已經更新了延遲的標示 */void Rotation( Node *cur, int direct ){Node *y = cur->child[!direct];Pushdown( y->child[0] );Pushdown( y->child[1] );Pushdown( cur->child[direct] );cur->child[!direct] = y->child[direct];cur->child[!direct]->parent = cur;y->parent = cur->parent;y->child[direct] = cur;if ( y->parent->child[0] == cur)y->parent->child[0] = y;elsey->parent->child[1] = y;cur->parent = y;//problem/** * 對cur進行更新,cur的子女節點可能有延遲的資訊,首先需要對cur原來的左子樹的子女節點進行pushdown操作 * 也需要對cur原來的右子女進行Pushdown操作 */Update( cur );Update( y );if ( root == cur )root = y;}//伸展到fp的子女節點void Splay( Node *cur, Node * fp ){/** * 保證cur的延遲資訊在旋轉之前更新到子女節點中 * 這樣,root-->cur路徑沒有任何延遲資訊 */Pushdown( cur );while ( cur->parent != fp ){if ( fp == cur->parent->parent ){if ( cur->parent->child[0] == cur )Rotation( cur->parent, 1 ); //right-rotationelseRotation( cur->parent, 0 );}else if ( cur->parent->child[0] == cur && cur->parent->parent->child[0] == cur->parent ){Rotation( cur->parent->parent, 1 );Rotation( cur->parent, 1 );}else if ( cur->parent->child[1] == cur && cur->parent->parent->child[1] == cur->parent ){Rotation( cur->parent->parent, 0 );Rotation( cur->parent, 0 );}else if ( cur->parent->child[0] == cur ){Rotation( cur->parent, 1 );Rotation( cur->parent, 0 );}else {Rotation( cur->parent, 0 );Rotation( cur->parent, 1 );}}Update( fp );}/** * 先更新延遲的標誌,再查詢,保證root到curNode都是最新的(沒有延遲標誌) */void Select( int rank, Node * fp ){//尋找第rank個元素,從1編號Node * curNode = root;Pushdown( curNode );while ( rank != curNode->child[0]->size+1 ){if ( rank <= curNode->child[0]->size )curNode = curNode->child[0];else {rank -= curNode->child[0]->size + 1;curNode = curNode->child[1];}Pushdown( curNode );}Splay( curNode, fp );}//add after posvoid InsertInterval( int pos, int *interval, int cnt ){// 第pos個元素稱為根節點Select( pos+1, null );// 第pos+1個元素成為根節點的右孩子Select( pos+2, root );//在root->child[1]的左孩子(為NULL)建立新子樹Node *p = MakeTree( interval, 0, cnt );root->child[1]->child[0] = p;p->parent = root->child[1];//伸展的同時更新整個路徑,一定要有Splay( root->child[1]->child[0], null );}void DeleteInterval( int left, int right ){Select( left, null );Select( right+2, root );root->child[1]->child[0] = null;Splay( root->child[1], null );}void Add( int left, int right, int data ){//砍樹[left,right],注意有一個頭結點哨兵Select( left, null );Select( right+2, root );root->child[1]->child[0]->delta += data;Splay( root->child[1]->child[0], null );}int GetMin( int left, int right ){Select( left, null );Select( right+2, root );return root->child[1]->child[0]->min;}//可以延遲旋轉void Reverse( int left, int right ){Select( left, null );Select( right+2, root );root->child[1]->child[0]->rev = !root->child[1]->child[0]->rev;Splay( root->child[1]->child[0], null );}void Revolve( int left, int right, int data ){++ left;++ right;int len;Node *x, *y;len = right-left+1;data = data%len;if ( 0 == data )return;Select( left, null );x = root;Select( left-1, null );Select( right+1, root );//left->right-data; right-data+1-->rightSelect( right-data, root->child[1] );y = root->child[1]->child[0];x->child[0] = y->child[1];x->child[0]->parent = x;y->child[1] = null;Splay( x, null );}int main(){int n, i, m, left, right, data, pos;char instruct[50];while ( scanf("%d", &n ) != EOF ){for ( i = 0; i < n; ++ i )scanf("%d", &seq[i] );InitTree();//在第一個元素(頭結點哨兵)後面加入InsertInterval( 0, seq, n );printf("%d\n", root->value );scanf("%d", &m);for ( i = 0; i < m; ++ i ){scanf("%s", instruct );if ( 'A' == instruct[0] ){scanf("%d%d%d", &left, &right, &data );Add( left, right, data );}else if ( 'I' == instruct[0] ){scanf("%d%d", &pos, &data );InsertInterval( pos, &data, 1 );}else if ( 'D' == instruct[0] ){scanf("%d", &pos );DeleteInterval( pos, pos );}else if ( 'M' == instruct[0] ){scanf("%d%d", &left, &right );printf("%d\n", GetMin( left, right ) );}else if ( 'E' == instruct[3] ){scanf("%d%d", &left, &right );Reverse( left, right );}else {scanf("%d%d%d", &left, &right, &data );Revolve( left, right, data );}//Print();}}return 0;}