这两天学习的欲望特别的。。。弱,脑子说“我转不动了,我不想转了,我想玩游戏”,心里说“才这么几天就放弃,年后依然找不到工作,没有钱哪有钱买游戏?” (╯‵□′)╯︵┻━┻ 在一番激烈斗争后,双方决定从心,能多学一点儿是一点儿吧,谁让以前总是脑子赢的下场不太好呢(╯‵□′)╯︵┻━┻。。。
伸展树
平衡二叉搜索树的一种形式,无需时刻严格保持全树的平衡,却能够在足够长的真实序列中保持分摊意义上的高效率。伸展树也不需要对二叉树的任意结点结构做任何改动,也不需要记录平衡因子或高度等额外信息,所以应用范围更广一些。
数据部局性:
1. 刚访问过的某个数据可能在不久后会被再次访问到;
2. 将被访问的下一个数据可能在不久之前访问过的另一个数据附近。
树中解决数据局部性的方法1:
简单伸展树:
每访问过一个结点后,反复以其父节点为轴经适当的旋转后将其提升一层,直至最后成为根结点。最坏的时间复杂度为O(n(2)), 平均时间复杂度为O(n)。比如像{5, 4, 3, 2, 1}这样的搜索树按照{1, 2, 3, 4, 5}的顺序访问的话。
为克服这种时间上的缺陷,采用双层伸展的策略。即每次都从当前结点v向上追溯两层,并根据其父结点p以及祖父结点g的相对位置进行相应旋转。
平均分摊时间为O(logn).
由于伸展树的查找会引起整棵树的结构调整,所以需要新的search方法,即在每次查找后无论成功与否都将终止处结点伸展至树根。
将结点v伸展至树根:
void AttachAsLChild(TreeNode p, TreeNode l)
{
p.left = l;
if(l != null)
{
l.parent = p;
}
}
void AttachAsRChild(TreeNode p, TreeNode r)
{
p.right = r;
if(r != null)
{
r.parent = p;
}
}
TreeNode Splay(TreeNode v)
{
if(v == null)
{
return NULL;
}
TreeNode p;
TreeNode g;
while(((p = v.parent) != null) && ((g = p.parent) != null))
{
TreeNode gg = g.parent;
//如果v,p,g同侧则为顺-顺(逆-逆)时针旋转,若异侧则为顺-逆(逆-顺)时针旋转
if(v = p.left)
{
if(p = g.left)
{
AttachAsLChild(g, p.right);
AttachAsLChild(p, v.right);
AttachAsRChild(g, p);
AttachAsRChild(p, v);
}
else
{
AttachAsLChild(p, v.right);
AttachAsRChild(g, v.left);
AttachAsLChild(v, g);
AttachAsRChild(v, p);
}
}
else if(p = g.right)
{
AttachAsRChild(p, v.left);
AttachAsRChild(g, p.left);
AttachAsLChild(v, p);
AttachAsLChild(p, g);
}
else
{
AttachAsLChild(g, v.right);
AttachAsRChild(p, v.left);
AttachAsLChild(v, p);
AttachAsRChild(v, g);
}
if(gg == null) //这一条件表示v已经被换到了根部
{
v.parent = null;
}
else
{
if(g == gg.left)
{
AttachAsLChild(gg, v);
}
else
{
AttachAsRChild(gg, v);
}
}
}
//循环结束时必有g为空,但p不一定为空,p可能为root
if(p != null)
{
if(v = p.left)
{
AttachAsLChild(p, v.right);
AttachAsRChild(v, p);
}
else
{
AttachAsRChild(p, v.left);
AttachAsLChild(v, p);
}
}
v.parent = null;
return v;
}
搜索:
每次搜索结束后将结果或最后停留的结果调用Splay()方法伸展至树根。
插入:
同样在插入前先搜索树中是不是没有一样的结点v,因为在搜索结束后已经将待插入的结点或其父结点t放在树根处了,所以可将树分为T1和T2两棵树,
如果v大于t,
tmpTree = t.right,
t.right = null,
v.left = t,
v.right = tmpTree
如果v小于t则倒过来。
删除:
删除的道理和插入差不多,因为也是在搜索结束后待删除的点已被移到树根,所以将这棵树分为左右两棵树TL和TR,然后取TR里最小的结点伸展至树根,组成一个新树。