左偏树

左偏树

啥是左偏树呢?

左偏树

大概是这样的

左偏树

这里需要注意两点:

  1. 左偏树是一棵二叉树,也是一种可并堆,拥有堆的性质,可以像堆一样合并。
  2. 左偏树顾名思义,有“左偏”的特点,既每个左子树节点的dist一定大于等于右子树节点的dist。

就比如像是什么\(priority \_ queue\)啥的既支持插入一个元素,还能查询最小/大元素,这直接STL就可以操作。但是要是某些毒瘤题目里让我们来使两个堆合并(其实算是正常操作吧),就不能用STL的\(priority\_queue\)来肝了【前提是不算\(pb\_ds\)那种神仙专属库】

有关左偏树的某些定义

距离

我们不妨设(能且仅能设)\(dist[i]\)表示一个节点它到离它最近的外节点的距离。

叶子节点的dist=0,空节点的dist=0

外节点

我们定义一个节点为外节点,当且仅当这个节点的左子树和右子树中的一个是空节点。(注意外节点不是叶子节点)

一些神奇的操作

合并

因为左偏树节点不满足完全二叉树而且十分左偏的性质,我们可以在一个log的复杂度中合并两个堆。

这个合并分为下面几步:

  1. 1:若是其中一个堆是空的,直接return 另一个堆
    2:否则选择一个堆顶val较大的堆作为父亲节点
    3:(如果是val相等那么使用堆顶dist相对比较大的堆作为父亲节点)
  2. 那么现在我们将根节点的右子树和另一个堆递归合并即可
  3. 如果左右儿子dist需要修正那么交换左右儿子
  4. 更新根节点dist
插入一个新的元素

一个新的元素就是一个只有一个节点的左偏树,直接合并这个左偏树和原先的左偏树即可。

弹出栈顶元素

我们可以合并根节点的左右子树,然后删除根节点

\(Code\)

#include <iostream>
#include <cstdio>
using namespace std;
inline int read() {
    int num = 0;
    char ch = getchar();
    bool flag = false;
    while (!isdigit(ch)) flag = ch == '-', ch = getchar();
    while (isdigit(ch)) num = (num << 1) + (num << 3) + (ch ^ 48), ch = getchar();
    return flag ? -num : num;
}
template<typename Tp>
struct Heap_node {
    int lson, rson;
    int dist;
    Tp val;
    Heap_node() {
        lson = rson = dist = 0;
    }
};
const int SIZE = 1000086;
struct Heap {
    Heap_node<int> tree[SIZE];
    int rt, cnt, tot;
    inline int New(int val) {
        ++tot;
        tree[tot].val = val;
        return tot;
    }
    inline void set_dist(int n) {
        tree[n].dist = tree[n].rson ? (tree[tree[n].rson].dist + 1): 0;
    }
    int merge(int a, int b) {
        if (a == 0 || b == 0)
            return a + b;
        else if (tree[a].val > tree[b].val)
            swap(a, b);
        else if (tree[a].dist < tree[b].dist)
            swap(a, b);
        tree[a].rson = merge(tree[a].rson, b);
        if (tree[a].lson != 0 && tree[a].rson != 0) {
            if (tree[tree[a].lson].dist < tree[tree[a].rson].dist)
                swap(tree[a].lson, tree[a].rson);
        } else if (tree[a].lson == 0 && tree[a].rson != 0)
            swap(tree[a].lson, tree[a].rson);
        set_dist(a);
        return a;
    }
    inline void insert(int val) {
        cnt++;
        int b = New(val);
        rt = merge(rt, b);
    }
    inline void pop() {
        if (rt == 0)
            return;
        else {
            cnt--;
            int a = tree[rt].lson, b = tree[rt].rson;
            rt = merge(a, b);
        }
    }
    inline int top() {
        return rt ? tree[rt].val : 0;
    }
    inline size_t size() {
        return cnt;
    }
    Heap() {
        rt = tot = cnt = 0;
    }
} h;
int n = read();

int main() {
    while (n--) {
        int opt = read();
        switch (opt) {
            case 1:
                h.insert(read());
                break;
            case 2:
                printf("%d\n", h.top());
                break;
            case 3:
                h.pop();
                break;
            default:
                break;
        }
    }
    return 0;
}
上一篇:11.23 考试总结


下一篇:HDU 6609 离散化+权值线段树