這兩天學習的慾望特別的。。。弱,腦子說“我轉不動了,我不想轉了,我想玩遊戲”,心裏說“才這麼幾天就放棄,年後依然找不到工作,沒有錢哪有錢買遊戲?” (╯‵□′)╯︵┻━┻ 在一番激烈鬥爭後,雙方決定從心,能多學一點兒是一點兒吧,誰讓以前總是腦子贏的下場不太好呢(╯‵□′)╯︵┻━┻。。。
伸展樹
平衡二叉搜索樹的一種形式,無需時刻嚴格保持全樹的平衡,卻能夠在足夠長的真實序列中保持分攤意義上的高效率。伸展樹也不需要對二叉樹的任意結點結構做任何改動,也不需要記錄平衡因子或高度等額外信息,所以應用範圍更廣一些。
數據部局性:
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裏最小的結點伸展至樹根,組成一個新樹。