treap 学习小记

Posted limil

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了treap 学习小记相关的知识,希望对你有一定的参考价值。

介绍

treap是tree和heap的组合词,说明这种数据结构有树的特点又有堆的特点。本质是一颗二叉搜索树。

treap的结点除了key关键字外还有个priority关键字。treap除了要保证key满足二叉搜索树性质,还要保证当前priority大于等于两个子节点的priority(即堆的性质)。由于priority是随机产生的值,故treap的结构是期望平衡的。

treap分为有旋treap和无旋treap。有旋treap就是带旋转操作的treap;无旋treap就是利用merge和split两个操作来解决各种问题。

有旋treap旋转常数比splay树小且好写,但好像不支持区间操作(还不清楚为什么);无旋treap好写功能强大,但比较慢。

无旋treap

无旋treap有两个核心操作,merge和split。

split操作

split操作接受根节点u和值key,把一个u指向的treap拆成两个treap,其中一个treap中每个结点的关键值小于等于key,另一个treap的关键值则大于key。
递归判断分裂,看代码会好理解。

PII split(int rt, int key) {  //返回的o.first小于等于,o.second为大于。
    if (!rt) {
        return mp(0, 0);
    }
    if (key < val[rt]) {
        PII o = split(lc[rt], key);
        lc[rt] = o.second;
        return mp(o.first, rt);
    } else {
        PII o = split(rc[rt], key);
        rc[rt] = o.first;
        return mp(rt, o.second);
    }
}

merge操作

merge操作接受两个根节点u和v,把u指向的treap和v指向的treap合并成一个treap。必须要保证u中所有结点的关键值小于v中所有结点。merge就是split的逆操作。由于两个treap之间是有序的,所以根据priority来递归merge,需满足大根堆性质。

int merge(int lrt, int rrt) {
    if (!lrt)
        return rrt;
    if (!rrt)
        return lrt;
    if (pos[lrt] > pos[rrt]) { //这里的pos就是priority,我换了个名字
        rc[lrt] = merge(rc[lrt], rrt);
        return lrt;
    } else {
        lc[rrt] = merge(lrt, lc[rrt]);
        return rrt;
    }
}

build

先判断插入的新结点是否已存在,如果不存在,就按新结点的关键值分裂成两个treap,然后再把结点夹进去merge。(split和merge真是太直观了)
建树好像有个O(n)的笛卡尔建树方法。

void insert(int& rt, int v) {
    if (!find(rt, v)) {
        si++;
        pos[si] = rand();
        val[si] = v;
        PII o = split(rt, v);
        rt = merge(merge(o.first, si), o.second);
    }
}

erase

erase之前必须保证当前的值在treap中存在,然后分裂成3份,去掉待删除的部分再合并起来。insert的逆操作

void erase(int& rt, int v) {
    if (!find(rt, v)) return ;
    PII o = split(rt, v);
    rt = merge(split(o.first, v - 1).first, o.second);
}

其它操作

查询操作就和普通二叉搜索树一样了。

代码

纯的无旋treap

#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <cstring>
#include <string>
#include <stack>
#include <deque>
#include <cmath>
#include <iomanip>
#include <cctype>

#define endl ‘
‘
#define IOS                           std::ios::sync_with_stdio(0);     cout.tie(0);                      cin.tie(0);
#define FILE freopen("..//data_generator//in.txt", "r", stdin), freopen("res.txt", "w", stdout)
#define FI freopen("..//data_generator//in.txt", "r", stdin)
#define FO freopen("res.txt", "w", stdout)
#define pb cpush_back
#define mp make_pair
#define seteps(N) fixed << setprecision(N)

typedef long long ll;
using namespace std;
/*-----------------------------------------------------------------*/

#define INF 0x3f3f3f3f
const int N = 1e5 + 10;
const int M = 1e9 + 7;
const double eps = 1e-8;

int pos[N];
int lc[N], rc[N];
int val[N];
int si;

typedef pair<int, int> PII;

void print(int rt) {
    if(!rt) return ;
    print(lc[rt]);
    cout << val[rt] << " ";
    print(rc[rt]);
}

int find(int rt, int v) {
    if (!rt)
        return rt;
    if (v > val[rt]) {
        return find(rc[rt], v);
    } else if (v < val[rt]) {
        return find(lc[rt], v);
    } else
        return rt;
}

PII split(int rt, int key) {
    if (!rt) {
        return mp(0, 0);
    }
    if (key < val[rt]) {
        PII o = split(lc[rt], key);
        lc[rt] = o.second;
        return mp(o.first, rt);
    } else {
        PII o = split(rc[rt], key);
        rc[rt] = o.first;
        return mp(rt, o.second);
    }
}

int merge(int lrt, int rrt) {
    if (!lrt)
        return rrt;
    if (!rrt)
        return lrt;
    if (pos[lrt] > pos[rrt]) {
        rc[lrt] = merge(rc[lrt], rrt);
        return lrt;
    } else {
        lc[rrt] = merge(lrt, lc[rrt]);
        return rrt;
    }
}

void insert(int& rt, int v) {
    if (find(rt, v)) return ;
    si++;
    pos[si] = rand();
    val[si] = v;
    PII o = split(rt, v);
    rt = merge(merge(o.first, si), o.second);
}

void erase(int& rt, int v) {
    if (!find(rt, v)) return ;
    PII o = split(rt, v);
    rt = merge(split(o.first, v - 1).first, o.second);
}



int main() {
    IOS;
    
}

有旋treap

没有具体地去实现qwq,有机会再补吧。

例题

参考

oiwiki



以上是关于treap 学习小记的主要内容,如果未能解决你的问题,请参考以下文章

算法学习Fhq-Treap(无旋Treap)

python学习小记

java学习中,DVD管理系统纯代码(java 学习中的小记录)

可持久化Treap(fhq Treap,非旋转式Treap)学习(未完待续)

java学习中,匿名函数构造方法构造代码块构造方法中调用构造方法(java 学习中的小记录)

平衡树合集(Treap,Splay,替罪羊,FHQ Treap)