• 左偏树


    左偏树

    啥是左偏树呢?

    大概是这样的

    这里需要注意两点:

    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
    ", h.top());
    				break;
    			case 3:
    				h.pop();
    				break;
    			default:
    				break;
    		}
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    缓存穿透、缓存雪崩、缓存击穿的区别和解决方案
    图解“红黑树”原理,一看就明白!
    Linux系统中常见文件系统格式
    Maven 加载ojdbc14.jar报错,解决方法
    mybatis中#{}和${}的区别
    SqlServer 分页批按时间排序
    Centos7安装与配置domain模式wildfly(默认配置)
    通过java调用Http接口上传图片到服务器
    Spring boot 配置array,list,map
    idea+springboot+freemarker热部署
  • 原文地址:https://www.cnblogs.com/gongcheng456/p/11746916.html
Copyright © 2020-2023  润新知