做了一天……內牛滿面……
參考資料:
http://dongxicheng.org/structure/splay-tree/
http://blog.csdn.net/liuwei_nefu/article/details/5901869
#include "stdio.h"
#include "stdlib.h"
typedef struct _Node{
int val;
int rev;
int child;
struct _Node* pre;
struct _Node* ch[2]; //0,左孩子; 1,右孩子;
}Node, *pNode;
int n, m;
pNode root;
/*更新節點的孩子數*/
void update(pNode r){
int lf, rt;
lf = r->ch[0]?(r->ch[0]->child+1):0;
rt = r->ch[1]?(r->ch[1]->child+1):0;
r->child = lf+rt;
}
// void insert(pNode* r, int data){
// pNode w = *r;
// if(!w){
// w = (pNode)malloc(sizeof(Node));
// w->val = data;
// w->child = 0;
// w->rev = 0;
// w->pre = 0;
// w->ch[0] = w->ch[1] = 0;
// *r = w;
// return;
// }
// if(data > w->val){
// insert(&(w->ch[1]), data);
// if(w->ch[1]->child==0) w->ch[1]->pre = w;
// }else{
// insert(&(w->ch[0]), data);
// if(w->ch[0]->child==0) w->ch[0]->pre = w;
// }
// update(w);
// }
/*旋轉,0爲往左旋轉,1爲往右旋轉。旋轉要注意各種指針的更新,要仔細啊!*/
void rotate(pNode* r, int op){ //0,lf; 1,rt;
pNode t, w = *r;
t = w->ch[!op]; //獲得左/右孩子
w->ch[!op] = t->ch[op];
if(t->ch[op]) t->ch[op]->pre = w;
t->ch[op] = w;
t->pre = w->pre;
if(w->pre){ //更新w父節點的孩子指針
if(w == w->pre->ch[0])
w->pre->ch[0] = t;
else
w->pre->ch[1] = t;
}else
root = t; //這句非常重要!!!如果t旋轉到了最頂點,即w->pre爲0,那麼要更新root指向!
w->pre = t;
update(w);
update(t);
*r = t; //將新的節點賦值到*r中
}
/*如果r被標記,則旋轉r的“直接”子樹,並將標記下傳到“直接”孩子*/
void push(pNode r){
pNode t;
if(r && r->rev){
t = r->ch[0];
r->ch[0] = r->ch[1];
r->ch[1] = t;
r->rev = 0;
if(r->ch[0])
r->ch[0]->rev ^= 1;
if(r->ch[1])
r->ch[1]->rev ^= 1;
}
}
/*尋找中序遍歷的第k個節點*/
pNode find(int k, pNode r){
int t;
if(!r) return 0;
push(r);
t = r->ch[0]?(r->ch[0]->child+1):0;
if(k==t+1)
return r;
if(k>t+1)
return find(k-t-1, r->ch[1]);
else
return find(k, r->ch[0]);
}
/*伸展運動,將a旋轉到b的下面。當b爲0時,即,將a旋轉爲root*/
void splay(pNode a, pNode b){
pNode k;
k = a->pre;
if(!k || k==b)
return;
if(a==k->ch[0])
rotate(&k, 1);
else
rotate(&k, 0);
splay(k, b);
}
// void show(pNode r){
// if(!r) return;
// show(r->ch[0]);
// printf("%d ", r->val);
// show(r->ch[1]);
// }
/*刪除樹,養成好習慣。不過要稍微多耗一點時間*/
void del(pNode r){
if(!r) return;
del(r->ch[0]);
del(r->ch[1]);
free(r);
}
/*將a~b切下來,放到c的後面*/
void cut(){
int a, b, c;
pNode tmp;
scanf("%d %d %d", &a, &b, &c);
splay(find(a, root), 0); //將實際的第a-1個節點旋轉爲root
splay(find(b+2, root), root); //將實際的第b+1個節點旋轉爲root的下面,即爲root的右孩子(因爲a<=b),這樣在a-1與b+1之間,即爲a~b,也就是b+1節點的左孩子!
tmp = root->ch[1]->ch[0]; //將b+1節點的左孩子切下來!
root->ch[1]->ch[0] = 0;
update(root->ch[1]); //更新切下後的節點
update(root);
splay(find(c+1, root), 0); //將實際的第c個節點旋轉爲root
splay(find(c+2, root), root); //將實際的第c+1個節點旋轉到root,即,第c個節點的下方。這時,第c+1個節點的左孩子肯定爲空!因爲比c+1小1的c,已經爲root了!
root->ch[1]->ch[0] = tmp; //將前面切下的接到第c+1個節點的左孩子
tmp->pre = root->ch[1];
update(root->ch[1]); //更新接上節點後的節點
update(root);
}
/*反轉!*/
void rvs(){
int a, b;
scanf("%d %d", &a, &b);
splay(find(a, root), 0); //旋轉爲root
splay(find(b+2, root), root); //旋轉到root的下面,那麼a-1與b+1之間,即b+1節點的左孩子,就是a~b
root->ch[1]->ch[0]->rev ^= 1; //只將b+1節點的直接左孩子標記即可,除非被下次訪問到,否則不需要馬上反轉!請看參考資料。
}
int mk;
void out(pNode r){
if(!r) return;
push(r);
out(r->ch[0]);
if(r->val>0 && r->val<n+1){
if(mk)
mk = 0;
else
printf(" ");
printf("%d", r->val);
}
out(r->ch[1]);
}
/*使用二分初始化樹,這樣可以使樹高度最低!如果採用直接右插,建好的樹深度爲n+2,會爆棧溢出!*/
void init(pNode* r, int st, int ed, pNode pre){
pNode w;
int m = (st+ed)>>1;
if(st>ed) return;
w = (pNode)malloc(sizeof(Node));
w->val = m;
w->pre = pre;
w->rev = 0;
w->child = ed-st;
w->ch[0] = w->ch[1] = 0;
*r = w;
init(&(w->ch[0]), st, m-1, w);
init(&(w->ch[1]), m+1, ed, w);
}
void main(){
char opt[5];
int i;
freopen("in.txt", "r", stdin);
while(scanf("%d %d", &n, &m), !(n<0&&m<0)){
root = 0;
init(&root, 0, n+1, 0); //注意啦!這裏插入的是0~n+1,共n+2個節點!爲啥呢?是爲了方便cut操作!所以如果要找第a個節點,實際要find第a+1個
for(i=0; i<m; i++){
getchar();
scanf("%s", opt);
if(opt[0]=='C')
cut();
else
rvs();
}
mk = 1;
out(root);
printf("\n");
del(root);
}
}