本蒟蒻最近剛剛學會平衡樹,特來寫篇部落格以加深印象。
(我的意思是若寫的不好望各位奆佬多多包含!)
插入 x 數
刪除 x 數(若有多個相同的數,因只刪除乙個)
查詢 x 數的排名(排名定義為比當前數小的數的個數 +1 。若有多個相同的數,因輸出最小的排名)
查詢排名為 x 的數
求 x 的前驅(前驅定義為小於 x ,且最大的數)
求 x 的後繼(後繼定義為大於 x ,且最小的數)
輸入格式:
第一行為 n ,表示操作的個數,下面 n 行每行有兩個數 opt 和 x , opt 表示操作的序號
輸出格式:
對於操作 3,4,5,6 每行輸出乙個數,表示對應答案
輸入樣例#1:
10
1 106465
4 11 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
輸出樣例#1:
106465
84185
492737
樣例是真的大啊!=_=
大的都不像是樣例了!
好了,現在開始講解!
首先講解我用的變數吧!!
int ch[maxn][2] ;//存兒子,ch[0]存左兒子,ch[1]存右兒子
int f[maxn] ;//存爹
int size[maxn] ;//存這個點的子樹大小
int cnt[maxn] ;//存權值
int key[maxn] ;//存數值
int sz,root ;//整棵樹的大小和樹根
首先,一切從簡單的開始講起,首先說輸入(不想看的向下翻)
首先輸入乙個數n,然後下面n行,每行輸入乙個操作標記和數。
int n,opt,x;
scanf("%d",&n);
for (int i=1;i<=n;++i)
}
然後在依次講解操作
操作1:插入
以我之見,插入這個是最容易打錯的操作(我就打錯了好幾次)。
總體的思路是:如果遇到乙個與x數值相同的數,就把數的權值加一。
還有一些特殊的情況:
1.如果插入時樹為空樹:那麼就把整棵樹的長度加一,因為只有乙個點,所以要給他的左右兒子賦值為0 ;
2.如果插入時已經到了樹的最底端(也沒有找到),那麼就可以直接插入。
具體見**,**如下:
void insert(int x)
int now = root , fa = 0 ;
while(true)
fa = now ;
if(key[now] < x) now = ch[now][1] ;//如果數值比當前點的數字大,找右兒子
else now = ch[now][0] ;//反之,找左兒子
if(now == 0)
}}
操作2:刪除
刪除在我看來無異於大模擬 ,也許就是個大模擬吧!!
刪除,簡單來說,就是把這乙個節點的data(兒子,父親,祖宗)給clear(清除)掉
但是,這個還是分好多好多的情況的啊!
1.如果有不少相同的數,那麼就隨便踢掉乙個
2.如果要刪得數沒有孩子,那麼就直接刪掉(畢竟沒有孩子沒有多餘的牽掛啥的)
3.如果只有乙個獨生子,那麼就要學一下檀黎鬥神的操作,乾掉自己的爸爸,自己當爸爸!!
最後捏,我們只要改變各個父子關係,好讓要刪數的data銷聲匿跡就好了!
然後就是**:
void del(int x)
if(!ch[root][0] && !ch[root][1])
if(!ch[root][0])
if(!ch[root][1])
int leftbig = pre() , oldroot = root ;找到新根(數的前驅),然後儲存舊根
splay(leftbig) ;//讓新根當根
ch[root][1] = ch[oldroot][1] ;//把原來x的右兒子接到新根上。
f[ch[oldroot][1]] = root ;//這樣就吧x徹底刪了qwq
clear(oldroot) ;//刪除廢物資料
update(root) ;//更新資料
}
ps:clear和pre也是個函式,意思很常規,後面有**。
操作3:查詢x排名
如果x比當前的節點小,那麼就去左子樹找
反之,去右子樹找
如果相等的話怎麼辦??
如果相等的話就停下啊還怎麼辦!!
好吧,不皮了
int find(int x)操作4:找到排名為x的節點ans += cnt[now] ;
now = ch[now][1] ;
} }}
這個說實話煩人啊!操作3和操作4看的我是眼花繚亂啊!qwq~~我太弱了~~qwq t~t
和上面的思想差不多~~
如果當前點有左子樹,且x比左子樹的權值(占得排名)小的話,就搭乘前往左子樹的列車。
反之,就去右子樹。
int findx(int x)操作5:找前驅}}
操作6:找後繼(狠心遺棄)
看了這麼久,大傢伙會發現,右子樹》根》左子樹的小規律,所以,前驅就是最左邊的點 ,同理,後繼就是最右邊的點嘍!!
int pre()
好了,我們現在基本操作都講完了,現在該敘述一下中間涉及到的函式了
function1:clear(int x)
只用於將點刪除後的清零小工作~~
void clear(int x)很簡單,沒啥好講的
function2:get(int x)
作用就是查詢x是左兒子還是右兒子。
int get(int x)function3 :update(int x )/*講解一下:(其實都能看懂吧~~)
如果x爹的右兒子是x的話,就返回1
如果x爹的右兒子不是x的話,也就是x是x爹的做兒子的話,返回0
*/
唯一用途:修改後更新數值
code
void update(int x)
}
也比較簡單,於是就不住釋了~~~
function4:rotate(int x)
這個可是平衡樹的重點大戲
rotate沒理解好就和輸出輸出錯了乙個效果!!qwq、
具體思路:首先找到x關於x爹的關係,記做k關係(用get())
然後 將x與k關係相反的兒子 的父親 記做與(x爹)關係相同的兒子
然後再將和x關係相反的兒子 的父親 記做 x爹 的父親
將x爹的父親記做x
將x與k關係相反的兒子 記做 x爹
將x的父親記做x爹的父親(也就是x的爺爺)
有點混亂是不是呀??!!
正常,看看**,自己畫畫圖就好了!!!
void rotate(int x)function5 : splay(int x)update(old) ;update(x) ;//更新數字
}
就是多次的rotate嘍!
直接看《code》 吧
void splay(int x)
} root = x ;
}
完結散花!!!!
啥??完整**??
把上面組合一下應該就是了吧……………………
洛谷P3369 模板 普通平衡樹
插入x數 刪除x數 若有多個相同的數,因只刪除乙個 查詢x數的排名 排名定義為比當前數小的數的個數 1。若有多個相同的數,因輸出最小的排名 查詢排名為x的數 求x的前驅 前驅定義為小於x,且最大的數 求x的後繼 後繼定義為大於x,且最小的數 輸入格式 第一行為n,表示操作的個數,下面n行每行有兩個數...
洛谷P3369 模板 普通平衡樹 Treap
插入xxx數 刪除x xx數 若有多個相同的數,因只刪除乙個 查詢x xx數的排名 排名定義為比當前數小的數的個數 1 1 1。若有多個相同的數,因輸出最小的排名 查詢排名為x xx的數 求x xx的前驅 前驅定義為小於x xx,且最大的數 求x xx的後繼 後繼定義為大於x xx,且最小的數 總算...
題解 洛谷 P3369 模板 普通平衡樹
splay是像我這樣的小蒟蒻一開始學的平衡樹。雖然splay常數不小,但是功能十分全面,既可以當區間樹也可以當平衡樹 當然這兩者不可兼顧 看到題解裡一堆dalao寫陣列,但是splay本來是應該用指標實現的 據說這樣常數會小很多 於是蒟蒻就開始寫起了指標splay。出人意料,陣列splay我一遍a,...