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,且最小的數 總算...