題解 洛谷 P3369 模板 普通平衡樹

2022-03-22 16:45:33 字數 3408 閱讀 3792

splay是像我這樣的小蒟蒻一開始學的平衡樹。

雖然splay常數不小,但是功能十分全面,既可以當區間樹也可以當平衡樹(當然這兩者不可兼顧)

看到題解裡一堆dalao寫陣列,但是splay本來是應該用指標實現的(據說這樣常數會小很多),於是蒟蒻就開始寫起了指標splay。。。

出人意料,陣列splay我一遍a,指標我居然除錯了將近一年(真事)我果然太蒟了。似乎明白了大家為什麼都不用指標。

但是,從一遍遍的除錯之間,我還是學到了不少東西,比如如何增加**的可除錯性,如何考慮到方方面面以防止re。這可能也是一種進步。最後發現,其實指標splay也挺好(nan)打,更重要的是,指標也更方便理解。

為了希望大家少走點彎路.帶大家入除錯大坑本蒟蒻覺得有必要寫乙份詳細的指標splay題解.

大部分資料**於網路。

spaly最快了233

inline unsigned int size(tree x)/*防止x為空導致訪問null的資訊(re)*/
inline void pushup(tree x)//乙個節點的大小等於它的左子樹的大小+右子樹大小+本身的個數(可以有重複)
inline bool wson(tree son,tree par)//0為左兒子,一為右兒子

inline void buildfather(tree son,tree par,bool which)//0表示變為左兒子,1表示變為右兒子

旋轉操作是splay tree的核心操作

它通過旋轉在不破壞bst的性質的情況下,調整樹的結構。

圖中可以看出c>q>b>p>a.

我們的目標是將p轉到q的位置

直接交換肯定不行,這破壞了bst的性質。

事實上,我們可以看出,a與c的位置一定是固定的。

p,q是我們需要交換的節點,所以我們可以通過對b進行換位來保證bst的性質不被破壞。以右旋為例。右圖仍然有c>q>b>p>a.

這樣就成功實現了上旋的操作

個人習慣將左右旋轉寫在乙個函式中

inline void rotate(tree x)

splay是splaytree的核心操作(廢話)。

它通過rotate,單旋與雙旋來維護splay tree的深度。

如果不理解雙旋(或者是spaly教的忠實信奉著)可以參考網上資料

這裡因為是主要講解指標,所以不再贅述。

inline void splay(tree x,tree y)//將x轉到y的下方

pushup(x);//填坑,因為你每一次rotate都將x向上轉,那麼你的x的size一定會一直改變,所以在rotate裡面pushup(x)沒有意義。只需要在最後pushup(x)即可。

}

建樹時,當然可以預先讀入資料構建完美的splay.

這裡只給出insert.畢竟複雜度也沒差多少

inline void insert(int val)

for(tree x=root;x;x=x->ch[val>=x->val])//從root向下插入,每次判斷應當走哪邊

if(!x->ch[val>=x->val])//如果到了空,也就是沒有這個節點}}

同普通bst

inline void find(int val)

這是乙個比較複雜的點.

你當然也可以將前驅旋轉到根,後繼旋轉到右子樹,然後直接刪除右子樹的左子樹。但這樣需要提前插入inf與-inf(或特判),個人認為比較容易出錯,畢竟你的splay裡多了兩個節點,findkth(k),需要將k++(inf永遠比你大))

詳見注釋

inline int del(int val)

else//兩子樹都非空

}

這裡理解可能有些難度,可以考慮手玩一下。

這個不難,但是一定要畫出圖,不能想當然!

inline int findkth(int k)

return -2147483647;//出現未知錯誤 (樹的構建可能出現問題)(如果你其他地方正確,這裡並不會有用)

}

前驅就是比你小的最大的乙個

後繼就是比你大的最小的乙個

所以前驅就是左子樹的最右乙個

後繼同理

這裡只對於前驅做出說明

inline int pre(int val)

inline int nxt(int val)

#includeusing namespace std;

#define il inline

#define rg register

#define gi getint()

#define pi(k) putint(k)

#define gc getchar()

#define file(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)

il int getint()

il void putint(int k)

struct splaytree

}*root;

splaytree()

inline int size(tree x)

// #define size(x) (x?x->size:0)

inline void pushup(tree x)

inline void buildfather(tree son,tree par,bool which)

inline bool wson(tree son,tree par)

inline void rotate(tree x)

inline void splay(tree x,tree y)

pushup(x);

} inline void insert(int val)

for(tree x=root;x;x=x->ch[val>=x->val])

if(!x->ch[val>=x->val])

}} inline void find(int val)

inline int findkth(int k)

return -2147483647;

} inline void del(int val)

else

}inline int pre(int val)

inline int nxt(int val)

}bt;

int main(void)

return 0;

}

洛谷P3369 模板 普通平衡樹

本蒟蒻最近剛剛學會平衡樹,特來寫篇部落格以加深印象。我的意思是若寫的不好望各位奆佬多多包含!插入 x 數 刪除 x 數 若有多個相同的數,因只刪除乙個 查詢 x 數的排名 排名定義為比當前數小的數的個數 1 若有多個相同的數,因輸出最小的排名 查詢排名為 x 的數 求 x 的前驅 前驅定義為小於 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,且最小的數 總算...