做了一天……内牛满面……
参考资料:
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);
}
}