指標實現 Treap

2022-05-05 21:09:08 字數 3713 閱讀 9244

前置知識:二叉排序樹,堆。

應用場景:平衡樹。

我們都知道,二叉排序樹就是滿足「\(lch」(左兒子小於根節點,右兒子大於根節點)的二叉樹,一般情況下插入、刪除和搜尋的時間複雜度都為 \(\theta(\log n)\) ,非常快。但在特殊情況下,二叉排序樹可能會退化成鏈,時間複雜度也會變為 \(\theta(n)\) 。只有當二叉排序樹平衡時速度最優。

堆也很簡單,就是根節點大於子節點的完全二叉樹。

當二叉排序樹退化成鏈時速度會大打折扣,因此很多毒瘤出題者都喜歡卡它。但是既然能有這種題目,那麼肯定就有能解決的方法,當然也不排除出題者自己都沒有做出來。二叉排序樹平衡時時間複雜度為 \(\theta(\log n)\) 。所以,若要保證二叉排序樹的最大時間複雜度為 \(\theta(\log n)\) ,就要保證該二叉排序樹平衡。這就是是平衡樹。

平衡樹有很多種,本文就不再贅述,僅討論treap

二叉排序樹和堆都是二叉樹。既然堆就是完全二叉樹,何不利用它這個性質來保證二叉排序樹平衡呢?

於是tree(二叉排序樹)+heap(堆)=treaptreap橫空出世!

這裡我定義了乙個結構體treap來封裝,然後再treap內又定義了結構體node,表示節點。

struct treap

e[100005],*root,*cnt;

};

解讀:

e:儲存所有節點。

root:指向根節點的指標。

cnt:指向最後乙個插入的節點的指標。

如果沒能看懂各變數的作用也沒有關係,在後面的操作中會為您一一解答。

2-0-1 建構函式

node(){}

// 插入節點時用

node(const int &x)

優化

c++自帶的rand函式速度較慢,我們可以自己寫乙個mrand函式來取代:

inline unsigned mrand()

然後建構函式如下:

node(){}

// 插入節點時用

node(const int &x)

2-0-2 更新節點資訊

不解釋:

inline void pushup()

現在node實現如下:

struct node

node(const int &x)

inline void pushup()

};

下面實現操作的函式均為treap的成員函式。

附:除錯函式,輸出當前樹的詳細資訊:

// now 表示所操作子樹的根節點指標引用,indent 表示當前縮排長度

void output(node *&now,const int &indent)

// 初始函式(方便呼叫)

inline void output()

旋轉是很多平衡樹常見的操作,分為左旋和右旋。

左旋的操作如下:

右旋的操作與此類似,僅方向不同。事實上,右圖中的樹右旋後即可得到左圖。

實現的**也很簡單:

// now 表示所操作子樹的根節點指標引用,d 表示旋轉方向(0 表示右旋,1 表示左旋)

// 這裡 now 為引用型別,便於更改。

inline void rotate(node *&now,const bool &d)

那麼為什麼要旋轉呢?因為每個節點都會有乙個隨機優先值,而treap的每個節點的優先值都比其子節點的大,利用堆的思想,使得treap相對平衡。

treap的插入其實就是在二叉排序樹的插入的基礎上通過旋轉保證treap堆的性質。

// now 表示指向當前節點的指標的引用,x 表示要插入的值

// 這裡 now 為引用型別,便於更改。

void insert(node *&now,const int &x)

++(now->size); // 因為新節點在以 now 指向的節點為根節點的子樹內,所以當前子樹的節點數+1

// 如果當前節點的數值不等於 x

if(now->val!=x)

// 否則說明當前節點的數值等於 x

else ++(now->times); // 當前節點的數值的存在數量+1

now->pushup(); // 更新當前節點(之前我沒有加上,導致我調了好久的 bug)

}// 初始函式(方便呼叫)

inline void insert(const int &x)

首先找到要刪除的節點,然後通過旋轉將其下移,直到其沒有子節點時之間再將其直接刪除。

// now 表示指向當前節點的指標的引用,x 表示要刪除的值

// 這裡 now 為引用型別,便於更改。

void remove(node *&now,const int &x)

now->pushup();// 更新當前節點

}// 初始函式(方便呼叫)

inline void remove(const int &x)

這個與二叉排序樹的操作一樣。

// now 表示指向當前節點的指標的引用,x 表示要查詢的值

int getrank(node *&now,const int &x)

// 初始函式(方便呼叫)

inline int getrank(const int &x)

// now 表示指向當前節點的指標的引用,x 表示要查詢的排名

int getval(node *&now,const int x)

// 初始函式(方便呼叫)

inline int getval(const int &x)

// now 表示指向當前節點的指標的引用,x 表示要查詢的數值

int getprev(node *&now,const int &x)

// 初始函式(方便呼叫)

inline int getprev(const int &x)

與查詢前驅思路相同。

// now 表示指向當前節點的指標的引用,x 表示要查詢的數值

int getnext(node *&now,const int &x)

// 初始函式(方便呼叫)

inline int getnext(const int &x)

這是一道模板題,沒什麼好說的,直接上**:

Treap原理和實現方法

分類 資料結構 2013 09 07 14 11 320人閱讀收藏 舉報treap是一棵二叉搜尋樹,只是每個節點多了乙個優先順序fix,對於每個節點,該節點的優先順序小於等於其所有孩子的優先順序。當然,引入優先順序fix的目的就是防止bst退化成一條鏈,從而影響查詢效率。所以,這樣看來就是 trea...

Treap實現的名次樹

1.感覺之前邵叔叔教的做fib的那個支援刪除 其實刪除只是打標記,而且不支援插入。和查詢的排序二叉樹就是個靜態的名次樹嘛。我居然學到了這麼奇怪的資料結構。2.寫的時候坑了幾次 1 不要亂用引用,getkth裡那個引數o沒過腦子用了引用,結果把樹搞爛了,一堆ch x 賦值成null了。2 寫查詢的時候...

Treap的實現方法 BZOJ 3224

傳說,有一種排序二叉樹叫做treap。而 treap tree heap 所以,treap既具有樹,也具有堆的性質。它的基本操作和普通的樹相近,但也有一些差異。以上全部為亂講系列 如果要看詳細介紹,這裡給出lmy大神關於平衡樹的研究講解 詳細介紹之後會補充的 首先,它的儲存方式和其他的二叉樹類似,都...