Treap樹堆C++實現
Treap
- Treap
樹堆(Treap = Tree + Heap)
,指的是有一棵擁有鍵值、優先級兩種權值且滿足堆的性質的二叉搜索樹
,其結構相當於以隨機數據插入的二叉搜索樹
,若每個結點的優先級事先給定且互不相等,那麼整棵樹的形態也唯一確定,與元素插入順序無關
,每個結點的優先級是隨機確定的,因此個操作的時間複雜度也隨機地,但插入、刪除、查找的期望時間複雜度均爲O(logn)
,其左右子樹也是Treap。通過使二叉搜索樹滿足堆的性質來儘量避免出現構造出單支樹(時間複雜度退化爲O(n))的情況,通過隨即優先級打破有序序列的構造序列
- 算法思路
Treap通過隨機地優先級和旋轉來維護Treap的性質,與平衡二叉樹不同的是,Treap只需要兩種旋轉方式,向左旋轉和向右旋轉
算法
旋轉
void Treap::leftRotate(Node* &p) {
Node *k = p->right;
p->right = k->left;
k->left = p;
p = k;
}
void Treap::rightRotate(Node* &p) {
Node *k = p->left;
p->left = k->right;
k->right = p;
p = k;
}
- 思路
基於結點p,向左旋轉時,指針k記錄當前結點的右子樹,將k的左子樹作爲結點p新的右子樹,將結點p作爲k的左子樹,最後使p指向結點k,結點p必須是結點指針的引用,以保證結點p的父結點的左右孩子被改變
- 圖解
插入
void Treap::insert(Node* &p, int value) {
if (p == NULL) {
p = new Node(value, rand());
} else {
if (value == p->data) {
return;
} else if (value < p->data) {
insert(p->left, value);
} else {
insert(p->right, value);
}
if(p->left && p->left->priority > p->priority) {
rightRotate(p);
} else if(p->right && p->right->priority < p->priority) {
leftRotate(p);
}
}
}
- 思路
插入結點時,遞歸地,根據二叉搜索樹的性質找到結點應該插入的位置
,產生一個隨機數作爲優先級,將結點插入Treap中,然後判斷是否需要旋轉
,若當前子樹根節點的左子樹存在且優先級大於當前子樹優先級,則右旋轉,若當前子樹根節點的右子樹存在且優先級大於當前子樹優先級,則左旋轉,使得Treap維持堆的性質
刪除
- 思路
遞歸地,根據二叉搜索樹的性質找到待刪除結點的位置
,爲維護Treap保持堆的性質(優先級保持堆的性質,關鍵字保持二叉搜索樹的性質)
,分爲三種情況
- 待刪除結點爲葉節點
直接刪除
- 待刪除結點爲葉節點有且僅有一棵子樹
將其子樹代替待刪除結點爲葉節點即可
- 待刪除結點爲葉節點有兩棵子樹
將兩棵子樹中優先級級高的旋轉到根結點
,然後遞歸地在另一棵子樹中(待刪除節點作爲原子樹根已經旋轉到另一個子樹)
刪除待刪除節點
- 圖解
結點外的值爲優先級
查找
實現代碼
- 實現中爲了看到效果,在類中增加了一個成員函數用於返回Treap中的最小值
#include<bits/stdc++.h>
using namespace std;
struct Node {
int data;
Node *left;
Node *right;
int priority;
Node(int value, int level) : data(value), left(NULL), right(NULL), priority(level) {}
};
class Treap {
private:
Node *root;
void leftRotate(Node* &p);
void rightRotate(Node* &p);
void insert(Node* &p, int value);
void remove(Node* &p, int value);
public:
Treap();
void insert(int value);
void remove(int x);
bool search(int x);
int smallest();
};
Treap::Treap() {
root = NULL;
}
void Treap::leftRotate(Node* &p) {
Node *k = p->right;
p->right = k->left;
k->left = p;
p = k;
}
void Treap::rightRotate(Node* &p) {
Node *k = p->left;
p->left = k->right;
k->right = p;
p = k;
}
void Treap::insert(int value) {
insert(root, value);
}
void Treap::insert(Node* &p, int value) {
if (p == NULL) {
p = new Node(value, rand());
} else {
if (value == p->data) {
return;
} else if (value < p->data) {
insert(p->left, value);
} else {
insert(p->right, value);
}
if(p->left && p->left->priority > p->priority) {
rightRotate(p);
} else if(p->right && p->right->priority < p->priority) {
leftRotate(p);
}
}
}
void Treap::remove(int value) {
remove(root, value);
}
void Treap::remove(Node* &p, int value) {
if (p->data == value) {
if (p->left == NULL) {
p = p->right;
} else if (p->right == NULL) {
p = p->left;
} else {
if (p->left->priority > p->right->priority) {
rightRotate(p);
remove(p->right, value);
} else if (p->left->priority < p->right->priority) {
leftRotate(p);
remove(p->left, value);
}
}
} else {
if (value < p->data) {
remove(p->left, value);
} else {
remove(p->right, value);
}
}
}
bool Treap::search(int value) {
Node *p = root;
while (p) {
if (p->data == value) {
return true;
} else {
p = p->data < value ? p->right : p->right;
}
}
return false;
}
int Treap::smallest() {
Node* p = root;
int value;
while (p) {
value = p->data;
p = p->left;
}
return value;
}
int main(int argc, char const *argv[]) {
Treap treap;
int N;
scanf("%d", &N);
for (int i = 0; i < N; i++) {
int value;
scanf("%d", &value);
treap.insert(value);
}
for (int i = 0; i < N; i++) {
int value = treap.smallest();
printf("%d ", value);
treap.remove(value);
}
return 0;
}
輸入數據
10
20 19 28 34 123 8900 21334 4345 234 567
輸出數據
19 20 28 34 123 234 567 4345 8900 21334
鳴謝
《算法競賽入門經典訓練指南》
最後
- 由於博主水平有限,不免有疏漏之處,歡迎讀者隨時批評指正,以免造成不必要的誤解!