图解splay / splay模板 / p3369
文章目錄
- 前言
- 一、例題 p3369
- 二、思路及代碼
- 1.思路
- 2.代碼
前言
splay 是 tarjan老爺子的又一發明,解決了平衡樹中的很多問題
當然,splay的內容學起來也是很復雜,于是我試著用圖的方式將這個算法展示出來
splay實現的數據結構是結構體所維護的二叉樹,每個節點保存 父節點,兒子節點,節點權值,權值出現個數,和子樹大小這幾個信息
旋轉過程主要由update(), rotate(), splay() 即 更新函數、旋轉函數、splay函數三個函數實現,其分別用來:更新旋轉后的子樹規模,左旋以及右旋,強制更新至根節點
rotate:
可對照代碼一起理解
splay:
可簡要地分為兩種情況:
對照代碼:
這四個轉化并不是特別顯然,但用手畫畫還是容易理解的
然后是5個功能函數:find(), insert(), maxnext(), delete(), kth(),分別用來:查詢排名,二分插入,前繼后繼查找,刪除節點,查詢第k大,我們來一一介紹
find():
首先利用平衡樹性質遞歸查找節點,
然后進行splay變換,
排名即為左子樹大小
insert():
二分遞歸查找應插入位置,
然后開點,最后還有splay變換
maxnext():
首先對x進行find()查詢排名,
這樣x便處于根節點的位置,
其前繼和后繼節點也很明顯
delete():
刪除是一個較為復雜的操作,具體步驟如下:
首先找到這個數的前驅,把他Splay到根節點
然后找到這個數后繼,把他旋轉到前驅的底下
比前驅大的數是后繼,在右子樹
比后繼小的且比前驅大的有且僅有當前數
在后繼的左子樹上面,
因此直接把當前根節點的右兒子的左兒子刪掉就可以
kth():
查詢排名第k的數,
其實和find()也很類似,
利用平衡樹的性質,
二分查找即可
一、例題 p3369
題目鏈接:洛谷 p3369
二、思路及代碼
1.思路
很經典的平衡樹模板,直接套模板即可
借鑒了很多ybb的內容:https://www.cnblogs.com/cjyyb/p/7499020.html
2.代碼
代碼如下:
#include <iostream> using namespace std; const int maxn = 201000; struct splaytree {int fa, child[2], val, cnt, size; } t[maxn]; // 父節點,左右子節點,權值,權值出現次數,子樹大小 int root, cnt; void update(int x) {t[x].size = t[t[x].child[0]].size + t[t[x].child[1]].size + t[x].cnt; } void rotate(int x) {int y = t[x].fa;int z = t[y].fa;int k = (t[y].child[1] == x); // 用來判斷左旋還是右旋t[z].child[(t[z].child[1] == y)] = x;t[x].fa = z;t[y].child[k] = t[x].child[k ^ 1];t[t[x].child[k ^ 1]].fa = y;t[x].child[k ^ 1] = y;t[y].fa = x;update(y);update(x); } void splay(int x, int s) {while (t[x].fa != s) { // s 為目標根節點int y = t[x].fa, z = t[y].fa;if (z != s)(t[z].child[0] == y) ^ (t[y].child[0] == x) ? rotate(x) : rotate(y);rotate(x);}if (s == 0) root = x; } void find(int x) {int u = root;if (!u) return;while (t[u].child[x > t[u].val] && x != t[u].val)u = t[u].child[x > t[u].val];splay(u, 0); } void insert(int x) {int u = root, fa = 0;while (u && t[u].val != x) {fa = u;u = t[u].child[x > t[u].val];}if (u)t[u].cnt++;else {u = ++cnt;if (fa) t[fa].child[x > t[fa].val] = u;t[u].child[0] = t[u].child[1] = 0;t[cnt].fa = fa;t[cnt].val = x;t[cnt].cnt = 1;t[cnt].size = 1;}splay(u, 0); } int maxnext(int x, int f) {find(x); // 此時 x 是根節點int u = root; // f: 0 前繼, 1: 后繼if (t[u].val > x && f) return u;if (t[u].val < x && !f) return u;u = t[u].child[f];while (t[u].child[f ^ 1]) u = t[u].child[f ^ 1];return u; } void Delete(int x) {int last = maxnext(x, 0);int maxnet = maxnext(x, 1);splay(last, 0);splay(maxnet, last);int del = t[maxnet].child[0];if (t[del].cnt > 1) {t[del].cnt--;splay(del, 0);} elset[maxnet].child[0] = 0; } int kth(int x) {int u = root;while (t[u].size < x) return 0;while (1) { // 二分查找int y = t[u].child[0];if (x > t[y].size + t[u].cnt) {x -= t[y].size + t[u].cnt;u = t[u].child[1];} else if (t[y].size >= x)u = y;elsereturn t[u].val;} } int main() {// freopen("in.txt", "r", stdin);// freopen("out.txt", "w", stdout);int n;scanf("%d", &n);insert(1e9);insert(-1e9);while (n--) {int opt, x;scanf("%d%d", &opt, &x);if (opt == 1) insert(x);if (opt == 2) Delete(x);if (opt == 3) find(x), printf("%d\n", t[t[root].child[0]].size);if (opt == 4) printf("%d\n", kth(x + 1));if (opt == 5) printf("%d\n", t[maxnext(x, 0)].val);if (opt == 6) printf("%d\n", t[maxnext(x, 1)].val);}return 0; }總結
以上是生活随笔為你收集整理的图解splay / splay模板 / p3369的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ONNX转ms后,benchmark跑分
- 下一篇: P3369 【模板】普通平衡树(fhq