--在每次查詢之後對樹進行重構
,把被查詢的條目搬移到
離樹根近一些的地方。伸展樹應運而生。伸展樹是一種自調整形式的二叉查詢樹,它會
沿著從某個節點到樹根之間的路徑
,通過一系列的旋轉把這個節點搬移到樹根去。
•大家只需要記住,每次進行插入
/查詢的時候,都要把插入
/查詢的元素通過旋轉變到根的位置,
splay
的單次操作均攤複雜度就是
o(logn)的。
splay
的儲存
與treap不同的地方,
splay
裡面不需要
key(隨機附加域)這個東西,而需要額外記錄該節點的父親。
struct splay_node
•同樣,我們也建議通過開記憶體池的方式實現動態分配節點,以及自己宣告乙個假空指標
null
代替系統的
null
。
splay
的旋轉
•update函式和
treap
一樣•set_ch
函式// a->set_ch(wh,now); 將now接在a的wh上
為了方便更改兒子
void splay_node::set_ch(intwh,splay_node *child)
inline void rotate(splay_node *&now)
splay
的伸展•
所謂伸展,就是將某個點不停的向上旋轉,直到旋轉到某乙個規定的位置(通常是根)。
•如果當前點,父親,爺爺呈一條直線,我們先轉父親再**己
如果當前點,父親,爺爺扭曲,我們連續轉兩次自己。
inline void splay(splay_node *now, splay_node *tar)
其中now
是我們要伸展的點,
tar是我們的目標:當我們伸展到
now的父親為
tar的時候,就不再伸展了。如果我們想讓
now伸展到根,就把
tar設定為
null
。
splay
的插入
•和treap
類似。只不過插入了以後,不再需要維護堆的性質進行調整,而是直接把新節點伸展到根。
void insert(int value)
if (newone->value < now->value)
now = now->ch[0];
else
now = now->ch[1];
}if (last == null)
root = newone;
else
}
splay的刪除
•splay
的刪除方式和
treap
不太相同。
•首先找到要刪除的節點
,然後把它伸展
(splay)
到根的位置。再分三種情況
:1.
無左右兒子
,直接刪除節點。
2. 只有乙個兒子
,令獨生子變成根
,刪去該點。
3. 左右兒子都有。找到左子樹中權值最大的點,將其
splay
到左兒子的位置。然後將
整棵右子樹掛在左兒子的右邊。刪除節點。
inline splay_node *find(int value)//查詢
if (now != null) splay(now, null);
return now;
}
void del(int value)
if (now->ch[0] == null && now->ch[1] == null)//第一種
root = null;
else if (now->ch[1] == null)//第二種
else if (now->ch[0] == null)
else//第三種
}
splay
的區間操作
•splay
相比treap
的優勢是能夠更好的實現區間操作。
•在進行區間操作之前我們通常要把我們要進行操作的區間弄到一棵子樹上。
•我們要對區間
[l,r]
進行操作,我們首先獲得
l-1節點和
r+1節點。我們把
l-1節點
splay
到根,再把
r+1節點
splay
到根的右兒子位置。那麼現在根的右兒子的左兒子這整棵子樹就是我們要的區間。
•因為我們在
splay
的過程中,一直會進行
update
,所以每個節點維護的附加資訊(比如
size
,我們還可以加入
sum,
min,
max等等)都一直是正確的。訪問根節點右兒子的左兒子的資訊,就可以直接獲得區間資訊。
#include #include #include #include #include using namespace std;
const int inf = 1e9;
const int max_q = 1e5 + 10;
struct splay_node
int get_wh()
void set_ch(int wh, splay_node *child);
} pool[max_q], *root, *null;
void splay_node::set_ch(int wh, splay_node *child)
int top = 0;
inline splay_node *get_new(int value)
inline void rotate(splay_node *&now)
inline void splay(splay_node *now, splay_node *tar)
void insert(int value)
if (newone->value < now->value)
now = now->ch[0];
else
now = now->ch[1];
} if (last == null)
root = newone;
else }
inline splay_node *find(int value)
if (now != null) splay(now, null);
return now;
}inline int pre(int value)
else
now = now->ch[0];
} return ans;
}inline int nxt(int value)
else
now = now->ch[1];
} return ans;
}void del(int value)
if (now->ch[0] == null && now->ch[1] == null)
root = null;
else if (now->ch[1] == null)
else if (now->ch[0] == null)
else }
inline int get_rank(int value)
if (value < now->value)
now = now->ch[0];
else
left_size += now->ch[0]->size + now->cnt, now = now->ch[1];
} return -1;
}inline int kth(int k)
if (k <= _) now = now->ch[0];
else left_size = _ + now->cnt, now = now->ch[1];
} return -1;
}inline int get_num()
int main()
}}
(模板)Splay 平衡樹
不會講解,直接上板子,按照洛谷p3369的要求 include include include using namespace std const int maxn 1000000 int ch maxn 2 f maxn size maxn cnt maxn key maxn int nodecn...
平衡樹 Splay 模板
又是 機房最後乙個學spl ay splay splay的人 參考d al ao dalao dalao部落格 寫的非常好!最後放上我的 有個地方應該寫ch root 0 ch root 0 ch roo t 0 手殘寫成ch root 1 ch root 1 ch roo t 1 還查不出來t t...
文藝平衡樹 Splay
鏈結 splay板子題 結果還調了很久的題,這就是搞文化課的 顯然維護陣列下標,使得splay的中序遍歷始終為當前數列 值得注意 旋轉時始終要記得更新節點 注意更新root節點 每次寫都忘.jpg includeusing namespace std const int maxn 100000 10...