伸展树的旋转和伸展操作
来源:互联网 发布:java开发工作经历描述 编辑:程序博客网 时间:2024/05/01 12:49
伸展树(Splay Tree)是一种排序二叉树,其核心操作是伸展。所谓伸展就是把指定节点旋转至树根(同时保持排序二叉树性质)的过程。而伸展操作的基础就是旋转。
旋转是所有排序二叉树的基本操作,各种平衡二叉树想要维持其平衡性质都离不开旋转。旋转分为左旋和右旋。但实际上,如果指定节点为左儿子,那么它只能右旋;如果指定节点为右儿子,那么它只能左旋。所以如何旋转可以看作是节点本身的一种性质,而非由外界传参决定。
无论是左旋还是右旋,均是重新确定三对父子关系。上图右旋中,旋转前后有3对父子关系发生了改变,改变后分别是Gt、tP和PB;左旋也一样:Gt、tP和PB。当然祖父节点G不一定存在(P一定存在,因为只会对非根节点做旋转)。
同时,左旋和右旋从某种意义上是对称的。所以可以只用一个函数完成,就称之为旋转。
假设使用静态数组实现二叉树,同时将伸展树的节点定义成如下结构体:
struct node_t{ int parent; int child[2]; int sn; //本节点是左儿子还是右儿子 //0表示左,1表示右 //...... //其他域省略;}Node[SIZE]; //Node[0]不使用,用于模拟NULL指针
首先将确定父子关系的代码封装成一个函数,该函数的意思是将p节点的sn儿子设置为t
void _link(int p,int sn,int t){ Node[p].child[sn] = t; Node[t].parent = p; Node[t].sn = sn;}
则旋转操作可以写成:
void _rotate(int t){ int p = Node[t].parent; int sn = Node[t].sn; int osn = sn ^ 1; //确定3对父子关系 _link(p,sn,Node[t].child[osn]); _link(Node[p].parent,Node[p].sn,t); _link(t,osn,p);}
对节点t每完成一次旋转操作,t就会提升一层。不停的旋转,t自然就会达到树根。所以最简单的伸展操作可以这样写(该函数的涵义是在根为root的二叉树中将节点t提升至树根):
void _splay(int t,int& root){ while( Node[t].parent ) _rotate(t); root = t;}
但是,还有一种稍微复杂一点但效率更高的用于伸展的旋转方法,称为双旋操作或者之字形旋转或者zig-zag操作等等。双旋操作本质上就是两个旋转操作,所以根本不必费心去画图观察如何实现,只要明确调用旋转的条件即可。
双旋操作是指:如果t及其父亲p的排行(同为左儿子或者同为右儿子)相同,则先旋转p再旋转t;否则,连续旋转t两次。一个双旋可以将t提升两个层次,所以t必须有祖父节点才能进行双旋。当然,此处不必显示的封装一个双旋函数,只需在伸展里面写出即可。使用双旋的伸展函数如下:
void _splay(int t, int& root){ while(Node[t].parent){ int p = Node[p].parent; if ( p == root ){ _rotate(t); break; } if ( Node[p].sn == Node[t].sn ){ _rotate(p); _rotate(t); }else{ _rotete(t); _rotate(t); } } root = t;}
注意rotate(t)有多处重复,所以上述代码可以精简一下,变成:
void splay(int t, int& root){ while(Node[t].parent){ int p = Node[t].parent; if ( p != root ) Node[p].sn == Node[t].sn ? _rotate(p) : _rotate(t); _rotate(t); } root = t;}
有时候,我们需要将指定节点t伸展成指定节点p的儿子,于是将伸展操作修改如下。当参数p取0时,就是将t伸展成树根。
void splay(int t,int p,int& root){while( Node[t].parent != p ){int pp = Node[t].parent;if ( Node[pp].parent != p ) Node[pp].sn == Node[t].sn ? _rotate(pp) : _rotate(t);_rotate(t);}if ( 0 == p ) root = t;}
- 伸展树的旋转和伸展操作
- 伸展树(Splay Tree)的旋转
- 标题:伸展树的基本操作:
- 伸展树:双层伸展
- 伸展树的点点滴滴
- 伸展树的点点滴滴
- 伸展树的点点滴滴
- 伸展树的读书笔记
- 伸展树的实现
- 伸展树
- 伸展树
- 伸展树
- 伸展树
- 伸展树
- 伸展树
- 伸展树
- 伸展树
- 伸展树
- 【整理】Linux内核中的atoi,itoa等函数
- LauncherShortcuts 创建应用程序某个Activity的快捷方式
- oninput,onpropertychange,onchange的用法和区别
- JavaScript你所不知道的困惑(2)
- android 自定义webview 如何使用gps 如何用模拟的gps
- 伸展树的旋转和伸展操作
- java Servlet 监听器
- NSFileManager 方法
- 数位dp无前导零
- js前端导出excel表格
- 浏览器清浮
- 如何在PHP程序中使用FusionCharts创建JavaScript图表
- 漏洞都是怎么编号的CVE/CAN/BUGTRAQ/CNCVE/CNVD/CNNVD
- leetcode第一刷_Convert Sorted List to Binary Search Tree