上一篇里把源代码贴出来了!这里就一点点来分析!
首先第一步建树:
- #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指针在《运用伸展树解决数列维护问题》中讲到了,就是在提取一段区间时,为了更方便操作,人为了增加两个结点,这两个结点不能影响树的结构。
接着就是树的初始化了:
-
- 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
-
- #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;
-
-
- 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){
-
- }
-
- 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;
-
- int main(){
- int a[10]={0,9,8,7,6,5,4,3,2,1};
- int i;
-
- tree.init();
-
- for(i&#61;1;i<10;i&#43;&#43;){
- tree.insert(a[i],i);
- }
-
- tree.inorder(tree.head);
- return 0;
- }
这里的伸展树退化成了一棵维护区间的二叉查找树&#xff01;记住是维护区间&#xff0c;像线段树一样&#xff0c;不会数据进行排序&#xff0c;只会维护区间。由于伸展树跟线段树很像&#xff0c;所以有的代码写法很像线段树&#xff01;其本质我觉得是一样的&#xff0c;只是代码组织的方式不同罢了。当然上面这样我更容易理解&#xff01;
而实际上&#xff0c;如果不做伸展操作&#xff0c;加入的数据在树中就会变成如下的形式&#xff1a;链式的数据其性能的很低的&#xff0c;所以必须做伸展操作&#xff1a;
这恰恰与《运用伸展树解决数列维护问题》中描述的“在伸展树中对区间进行操作”是一样的&#xff01;而根结点的右孩子的左子树就是要维护的区间。