Treap的實現方法 BZOJ 3224

2021-08-01 22:23:57 字數 2719 閱讀 5808

傳說,有一種排序二叉樹叫做treap。

而 treap = tree + heap

所以,treap既具有樹,也具有堆的性質。

它的基本操作和普通的樹相近,但也有一些差異。

(以上全部為亂講系列)

(如果要看詳細介紹,這裡給出lmy大神關於平衡樹的研究講解

(詳細介紹之後會補充的)

首先,它的儲存方式和其他的二叉樹類似,都有關於該點以及它的子樹的儲存資訊。我們用 val 儲存每個點的點權,rnd (rd) 儲存每個點擁有的隨機優先順序,這樣我們就可以在之後利用堆的性質對其加以排序。同時我們也要記錄這個樹所包含的所有子樹以及它自己的大小,用 size 來表示。如果乙個點可以被多次建立,那麼我們還需要用 weight (wei) 來儲存當前點有多少是「重複」的 (即:這個點被重複插入了多少次)。

struct treap 

int comp (int x)

void maintain ()

};treap *root = null;

接下來是它的兩個基本操作,插入和刪除。

那麼問題來了:如何對其進行旋轉操作呢?這裡我們給出了一種方法。至於實現圖例,本人還沒有新增,先看**。

void rotate(treap *&t, int d)

void ins(treap *&t, int v) else else

}t->maintain();

}}void del(treap *&t,int v) else else

if (t->s[1] == null) else

}} else

if (t != null) //防止刪空

t->maintain();

}

那麼,這裡的基本修改操作已經介紹完了。

所以,如何查詢treap裡面的某些特定元素(某乙個數的排名,處於某乙個排名的數,某個特定的數的前驅/後繼)?

這裡我們就要用到堆的性質。

我們以查詢乙個數 k 的排名為例子,當 k 的值大於當前節點時,即搜尋它的右子節點,同時加上該節點和它的左子樹的 size 值(表示它的名次)。當 k 的值小於當前節點時,即搜尋它的左子樹,此時不加任何 size 值(k 的排名會在前面)。當 k 的值與當前節點相等時,返回 rank+1(名次要包括當前節點)。

其他的查詢方式與上述思想相似,這裡不再贅述。看**就能懂了。

int query_rank(treap *t, int v)

//如果這個位置沒有左子樹,那麼,這個數的相對名次(相對於這棵子樹而言)為0,返回的名次值就應為1

else

//返回所有左子樹的size值,名次也對應地增加這麼多

if (d == -1) else

if (d == 1) else

}int query_kth(treap *t, int k) else else

if (k > t->s[0]->size && k <= p) else

}}int query_prev(treap *t, int v, int m) else

}int query_next(treap *t, int v, int m) else

}

至此,我們已經明白了所有的操作方式。

下面貼上完整的**:

Treap原理和實現方法

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

Treap實現的名次樹

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

並查集 treap實現名次數 BZOJ2733

time limit 10 sec memory limit 128 mb submit 1351 solved 710 submit status 永無鄉包含 n 座島,編號從 1 到 n,每座島都有自己的獨一無二的重要度,按照重要度可 以將這 n 座島排名,名次用 1 到 n 來表示。某些島之間...