Day15

这两天学习的欲望特别的。。。弱,脑子说“我转不动了,我不想转了,我想玩游戏”,心里说“才这么几天就放弃,年后依然找不到工作,没有钱哪有钱买游戏?” (╯‵□′)╯︵┻━┻ 在一番激烈斗争后,双方决定从心,能多学一点儿是一点儿吧,谁让以前总是脑子赢的下场不太好呢(╯‵□′)╯︵┻━┻。。。

伸展树
平衡二叉搜索树的一种形式,无需时刻严格保持全树的平衡,却能够在足够长的真实序列中保持分摊意义上的高效率。伸展树也不需要对二叉树的任意结点结构做任何改动,也不需要记录平衡因子或高度等额外信息,所以应用范围更广一些。

数据部局性:
1. 刚访问过的某个数据可能在不久后会被再次访问到;
2. 将被访问的下一个数据可能在不久之前访问过的另一个数据附近。

树中解决数据局部性的方法1:
简单伸展树:
每访问过一个结点后,反复以其父节点为轴经适当的旋转后将其提升一层,直至最后成为根结点。最坏的时间复杂度为O(n(2)), 平均时间复杂度为O(n)。比如像{5, 4, 3, 2, 1}这样的搜索树按照{1, 2, 3, 4, 5}的顺序访问的话。

为克服这种时间上的缺陷,采用双层伸展的策略。即每次都从当前结点v向上追溯两层,并根据其父结点p以及祖父结点g的相对位置进行相应旋转。
zig-zig
zig-zag
zig/zag
平均分摊时间为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里最小的结点伸展至树根,组成一个新树。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值