上一篇里把源代码贴出来了!这里就一点点来分析!
首先第一步建树:
- #define INF ~0u>>1
- #define NIL SPLAY
- #define MN 200005
- using namespace std;
- int n,m,l,r,x,pos;
- char s[10];
- struct SPLAYTREE{
- struct NODE{
- int key,minv,size,add;
- bool rev;
- NODE *left,*right,*father;
- NODE (){}
- NODE(int _key):key(_key){minv=_key,size=1,add=0,rev=false;}
- }SPLAY[MN],*SP,*root,*head,*tail;
- }tree;
这句宏定义:
- #define INF ~0u>>1
的意思等同于:
- #define INF 0x7FFFFFFF
"u"是表示unsigned int,这里呢就是先把无符号整型0取反,然后右移一位。
第二点很难理解的就是: NODE(int _key):key(_key)
这句代码主要是理解C++中new关键字的应用:(http://sbp810050504.blog.51cto.com/2799422/1026483)这里面讲得挺好的!
head指针与tail指针在《运用伸展树解决数列维护问题》中讲到了,就是在提取一段区间时,为了更方便操作,人为了增加两个结点,这两个结点不能影响树的结构。
接着就是树的初始化了:
- //对伸展树进行初始化,也就是把head和tail加入到树中,
- void init(){
- SP=NIL;
- NIL->key=NIL->minv=INF;
- NIL->size=0;
- NIL->rev=false;NIL->add=0;//用于懒操作的
- NIL->left=NIL->right=NIL->father=NIL;
- head=new(++SP)Node(INF);
- head->left=head->right=head->father=NIL;
- tail=new(++SP)Node(INF);
- tail->left=tail->right=tail->father=NIL;
- head->right=tail;tail->father=head;
- head->size++;
- root=head;
- }
把初始化与前面的关键字new的用法结合起来,就能体会到作者C++语言的功底了:西漂亮,优雅!也能理解到SP指针是干嘛的了!
为了更好的理解,我把树打印出来:即先插入一段数据,然后按下标的顺序把数据打印出来!
- #include <iostream>
- #define INF ~0u>>1
- #define MN 200005
- #define NIL SPLAY
- using namespace std;
- struct SplayTree{
- struct Node{
- int key,minv,size,add;
- bool rev;
- Node *left,*right,*father;
- Node(){}
- Node(int _key):key(_key){minv=_key,size=1,add=0,rev=false;}
- }SPLAY[MN],*SP,*root,*head,*tail;
- //
- // //对伸展树进行初始化,也就是把head和tail加入到树中,
- void init(){
- SP=NIL;
- NIL->key=NIL->minv=INF;
- NIL->size=0;
- NIL->rev=false;NIL->add=0;//用于懒操作的
- NIL->left=NIL->right=NIL->father=NIL;
- head=new(++SP)Node(INF);
- head->left=head->right=head->father=NIL;
- tail=new(++SP)Node(INF);
- tail->left=tail->right=tail->father=NIL;
- head->right=tail;tail->father=head;
- head->size++;
- root=head;
- }
- //暂时只维护区间,不作伸展操作
- void splay(Node *&root,Node *&t){
- }
- //往第pos位后插入key
- void insert(int key,int pos){
- Node *t;t=new(++SP)Node(key);
- t->left=t->right=t->father=NIL;
- Node *r,*p;r=root;
- bool flag=false;//默认朝左边走
- while(r!=NIL){
- p=r;r->size++;
- if(r->left->size+1>pos)r=r->left,flag=false;
- else pos-=r->left->size+1,r=r->right,flag=true;
- }
- if(flag)p->right=t;
- else p->left=t;
- t->father=p;
- splay(root,t);
- }
- void inorder(Node *t){
- if(t->left!=NIL)
- inorder(t->left);
- printf("%d ",t->key);
- if(t->right!=NIL)
- inorder(t->right);
- }
- }tree;
- //这里需要注意的是,SplayTree一定要定义成全局变量,不然会因为栈溢出而报内存错误
- int main(){
- int a[10]={0,9,8,7,6,5,4,3,2,1};//a[0]位置的数据不用
- int i;
- //初始化
- tree.init();
- //插入数据
- for(i=1;i<10;i++){
- tree.insert(a[i],i);
- }
- //按下标的顺序输出
- tree.inorder(tree.head);
- return 0;
- }
这里的伸展树退化成了一棵维护区间的二叉查找树!记住是维护区间,像线段树一样,不会数据进行排序,只会维护区间。由于伸展树跟线段树很像,所以有的代码写法很像线段树!其本质我觉得是一样的,只是代码组织的方式不同罢了。当然上面这样我更容易理解!
而实际上,如果不做伸展操作,加入的数据在树中就会变成如下的形式:链式的数据其性能的很低的,所以必须做伸展操作:
这恰恰与《运用伸展树解决数列维护问题》中描述的“在伸展树中对区间进行操作”是一样的!而根结点的右孩子的左子树就是要维护的区间。