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裏最小的結點伸展至樹根,組成一個新樹。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章