学习笔记」左偏树

news2024/12/25 0:12:42

dist 的性质

对于一棵二叉树,我们定义左孩子或右孩子为空的节点为外节点,定义外节点的 distdist 为 11,空节点的 distdist 为 00,不是外节点也不是空节点的 distdist 为其到子树中最近的外节点的距离加一。
一棵根的 distdist 为 xx 的二叉树至少有 2x−12x−1 个节点。此性质所有二叉树都有,并非左偏树特有。
distdist 不是深度,左偏树的深度没有保证,一条向左的链也是左偏树。

左偏树的性质

左偏树是一棵二叉树,并且是“左偏”的,即每个节点左儿子的 distdist 都大于等于右儿子的 distdist。
因此,左偏树中每个节点的 distdist 是它右儿子的 distdist 加一。

变量

int lson[N], rson[N], fa[N], fat[N];
ll val[N], dist[N];

lson: 左孩子(左偏);
rson: 右孩子;
fa: 父节点;
fat: 祖先(并查集);
val: 权值;
dist: 就是 distdist。

操作

  • 合并

int merge(int x, int y) { // 合并
	if (!x || !y) {
		return x | y;
	}
	if (val[x] > val[y] || (val[x] == val[y] && x > y))
		swap(x, y);
	rson[x] = merge(rson[x], y);
	fat[rson[x]] = fa[rson[x]] = x;
	if (dist[lson[x]] < dist[rson[x]])
		swap(lson[x], rson[x]);
	dist[x] = dist[rson[x]] + 1;
	return x;
}

if (!x || !y) { return x | y; }
如果与空节点合并,则直接合并即可
if (val[x] > val[y] || (val[x] == val[y] && x > y))
说明这是个小根堆,小元素在上面。
if (dist[lson[x]] < dist[rson[x]]) swap(lson[x], rson[x]);
维护左偏的性质。

  • 删除任意一个节点

左偏树是不支持删除给定权值的点的,只能删除知道点的标号的点。

void earse(int u) { // 删除任意一点
	int tmp = merge(lson[u], rson[u]), fu = fa[u];
	fat[tmp] = fa[tmp] = fu;
	fat[u] = fa[u] = tmp;
	lson[fu] == u ? lson[fu] = tmp : rson[fu] = tmp;
	while (fu) {
		if (dist[lson[fu]] < dist[rson[fu]])
			swap(lson[fu], rson[fu]);
		if (dist[fu] == dist[rson[fu]] + 1)
			return ;
		dist[fu] = dist[rson[fu]] + 1;
		fu = fa[fu];
	}
}

int tmp = merge(lson[u], rson[u]), fu = fa[u]; 先将被删节点的左右孩子合并。
fat[tmp] = fa[tmp] = fu; 处理好父亲和孩子的关系。

while (fu) {
	if (dist[lson[fu]] < dist[rson[fu]])
		swap(lson[fu], rson[fu]);
	if (dist[fu] == dist[rson[fu]] + 1)
		return ;
	dist[fu] = dist[rson[fu]] + 1;
	fu = fa[fu];
}

删除点之后可能不符合左偏性质,需要我们向上修改,直到到根节点或符合左偏性质为止。

  • 查询 uu 点所在堆的堆顶元素的标号

这个操作类似于并查集操作。

int find(int u) { // 查询堆顶的元素的标号
	return (fat[u] == u || fat[u] == 0) ? u : fat[u] = find(fat[u]);
}
  • 删除 uu 点所在堆的堆顶元素

void pop(int u) { // 弹出 u 点所在对的堆顶元素
	int g = find(u);
	earse(g);
}
  • 查询 uu 点所在堆的堆顶元素

ll top(int u) { // 查询 u 点所在堆的堆顶元素
	int g = find(u);
	return val[g];
}
  • 建树操作

int build(int n) { // 建树
	queue<int> q;
	for (int i = 1; i <= n; ++ i) {
		q.push(i);
	}
	int x, y, z;
	while (q.size() > 1) {
		x = q.front(), q.pop();
		y = q.front(), q.pop();
		z = merge(x, y), q.push(z);
	}
	return q.front();
}

模板

// 左偏树(小根堆)
struct leftist_tree {
	int lson[N], rson[N], fa[N], fat[N];
	ll val[N], dist[N];

	int merge(int x, int y) { // 合并
		if (!x || !y) {
			return x | y;
		}
		if (val[x] > val[y] || (val[x] == val[y] && x > y))
			swap(x, y);
		rson[x] = merge(rson[x], y);
		fat[rson[x]] = fa[rson[x]] = x;
		if (dist[lson[x]] < dist[rson[x]])
			swap(lson[x], rson[x]);
		dist[x] = dist[rson[x]] + 1;
		return x;
	}

	int find(int u) { // 查询堆顶的元素的标号
		return (fat[u] == u || fat[u] == 0) ? u : fat[u] = find(fat[u]);
	}

	void earse(int u) { // 删除任意一点
		int tmp = merge(lson[u], rson[u]), fu = fa[u];
		fat[tmp] = fa[tmp] = fu;
		fat[u] = fa[u] = tmp;
		lson[fu] == u ? lson[fu] = tmp : rson[fu] = tmp;
		while (fu) {
			if (dist[lson[fu]] < dist[rson[fu]])
				swap(lson[fu], rson[fu]);
			if (dist[fu] == dist[rson[fu]] + 1)
				return ;
			dist[fu] = dist[rson[fu]] + 1;
			fu = fa[fu];
		}
	}

	ll top(int u) { // 查询 u 点所在堆的堆顶元素
		int g = find(u);
		return val[g];
	}

	void pop(int u) { // 弹出 u 点所在对的堆顶元素
		int g = find(u);
		earse(g);
	}

	int build(int n) { // 建树
		queue<int> q;
		for (int i = 1; i <= n; ++ i) {
			q.push(i);
		}
		int x, y, z;
		while (q.size() > 1) {
			x = q.front(), q.pop();
			y = q.front(), q.pop();
			z = merge(x, y), q.push(z);
		}
		return q.front();
	}
};

pb_ds 中的堆

__gnu_pbds :: priority_queue 

成员函数

 

push(): 向堆中压入一个元素,返回该元素位置的迭代器。
pop(): 将堆顶元素弹出。
top(): 返回堆顶元素。
size(): 返回元素个数。
empty(): 返回是否非空。
modify(point_iterator, const key): 把迭代器位置的 key 修改为传入的 key,并对底层储存结构进行排序。 
erase(point_iterator): 把迭代器位置的键值从堆中擦除。
join(__gnu_pbds :: priority_queue &other): 把 other 合并到 *this 并把 other 清空。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/893709.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

多线段的研究

1.AutoCAD分为二维多线段(命令pline)&#xff0c;三维多线段(3dpoly)。 1.1&#xff1a;二维多线段 对应类Acdb2dPolyline&#xff0c;有起点&#xff0c;末点的宽度。由Acdb2dVertex&#xff08;顶点组成&#xff09; 1.2:三维多义线&#xff0c;(三维多义线的顶点没有凸度&a…

地理测绘基础知识(3)-观测与遮挡

在上一篇文章中&#xff0c;我们介绍了椭球模型下的一系列基础的坐标操作。本节&#xff0c;介绍观测与遮挡问题。 观测主要用于从观察点A观测大地标准点B&#xff0c;用来解决观测的仰角、方位角与大地坐标系之间的关系。 在没有GPS卫星的时代&#xff0c;为了测量一个位置的…

ipad手写笔一定要买苹果的吗?适合学生党电容笔推荐

暑假接近尾声&#xff0c;不少学生党开始为开学而做准备了。如果你想要一个与iPad相匹配的电容笔&#xff0c;可以买一个Apple Pencil吧。但事实上&#xff0c;这个苹果产品性能比较出色&#xff0c;卖的还是很好的。但是平替电容笔也是个不错的选择&#xff0c;而且价格也很合…

构建LLM应用程序时需要了解的5件事

推荐&#xff1a;使用 NSDT场景编辑器 助你快速搭建可二次编辑的3D应用场景 1.幻觉 使用LLM时应注意的主要方面之一是幻觉。在LLM的背景下&#xff0c;幻觉是指产生不真实的&#xff0c;不正确的&#xff0c;无意义的信息。LLM非常有创意&#xff0c;它们可以用于不同的领域&am…

亚马逊如何登录多个买家号?如何防止账号关联?

如果有多买家账号需要登录使用&#xff0c;以下是在同一设备上登录多个买家账号的一般步骤&#xff1a; 1、登出当前账号&#xff1a;如果您已经登录了一个买家账号&#xff0c;首先需要退出该账号。在页面右上角&#xff0c;通常会看到一个"Hello, [您的用户名]"&a…

终端安全无忧!迅软科技助力母婴用品企业保护隐私信息

客户简要介绍 某母婴用品企业是专业的婴幼儿用品综合制造厂商&#xff0c;是总部设在上海&#xff0c;致力于研发集安全性、舒适性、功能性以及环保于一体的产品。 企业的重要诉求 公司内部奶瓶、纸尿裤等产品的销售数据以及新品设计图片要避免外传被竞争对手拿到&#xff0c;需…

Java“牵手”根据关键词搜索(分类搜索)1688商品列表页面数据获取方法,1688API实现批量商品数据抓取示例

1688商城是一个网上购物平台&#xff0c;售卖各类商品&#xff0c;包括服装、鞋类、家居用品、美妆产品、电子产品等。要获取1688商品列表和商品详情页面数据&#xff0c;您可以通过开放平台的接口或者直接访问1688商城的网页来获取商品详情信息。以下是两种常用方法的介绍&…

yaml语法规则

1.语法规则 大小写敏感属性层级关系使用多行描述&#xff0c;每行结尾使用冒号结束使用缩进表示层级关系&#xff0c;同层级左侧对齐&#xff0c;只允许使用空格&#xff08;不允许 使用Tab键&#xff09;属性值前面添加空格&#xff08;属性名与属性值之间使用冒号空格作为分…

Azure VM上意外禁用NIC如何还原恢复

创建一个windows虚拟机&#xff0c;并远程连接管理员的方式打开powershell 首先查看虚拟网卡&#xff0c;netsh interface show interface 然后禁用虚拟网卡 ,netsh interface set interface Ethernet disable 去Azure虚拟机控制台&#xff0c;打开串行控制台 控制台中键入cmd,…

如何使用 Docker Compose 运行 OSS Wordle 克隆

了解如何使用 Docker Compose 在五分钟内运行您自己的流行 Wordle 克隆实例。您将如何部署 Wordle&#xff1f; Wordle在 2021 年底发布后席卷了互联网。对于许多人来说&#xff0c;这仍然是一种早晨的仪式&#xff0c;与一杯咖啡和一天的开始完美搭配。作为一名 DevOps 工程师…

MongoDB 安装 linux

本文介绍一下MongoDB的安装教程。 系统环境&#xff1a;CentOS7.4 可以用 cat /etc/redhat-release 查看本机的系统版本号 一、MongoDB版本选择 当前最新的版本为7.0&#xff0c;但是由于7.0版本安装需要升级glibc2.25以上,所以这里我暂时不安装该版本。我们选择的是6.0.9版本…

Leetcode每日一题:1388. 3n 块披萨(2023.8.18 C++)

目录 1388. 3n 块披萨 问题描述&#xff1a; 实现代码与解析&#xff1a; 动态规划 原理思路&#xff1a; 1388. 3n 块披萨 问题描述&#xff1a; 给你一个披萨&#xff0c;它由 3n 块不同大小的部分组成&#xff0c;现在你和你的朋友们需要按照如下规则来分披萨&am…

【MT32F006】MT32F006之HT1628驱动LED

本文最后修改时间&#xff1a;2023年03月30日 一、本节简介 本文介绍如何使用MT32F006连接HT1628芯片驱动LED。 二、实验平台 库版本&#xff1a;V1.0.0 编译软件&#xff1a;MDK5.37 硬件平台&#xff1a;MT32F006开发板&#xff08;主芯片MT32F006&#xff09; 仿真器&a…

【实用黑科技】如何 把b站的缓存视频弄到本地——数据恢复软件WinHex 和 音视频转码程序FFmpeg

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;效率…

Mathematica(42)-计算N个数值的和

比如&#xff0c;我们要用Mathematica求得到下面的式子&#xff1a; 这就需要用到一个函数&#xff1a;Sum 具体地&#xff0c;Sum函数的使用形式如下&#xff1a; 因此&#xff0c;按照公式就可以得到下面的结果&#xff1a; 如果&#xff0c;我们想要将求和号也加进去&#…

三肽-32——修复受损肌肤

简介 昼夜节律是自然界最普遍的一种自然现象, 它的存在使生物体的生理、生化、行为等生命现象表现为以24小时为周期的振荡。昼夜节律发生的物质基础是分子计时器, 即昼夜节律生物钟(circadian clock) 。它由一组特异的核心元件组成, 包括CLOCK (Circadian Locomotor Output Cy…

公路桥梁有哪些安全隐患?

在现代社会&#xff0c;公路桥梁作为连接城市、串联交通的重要纽带&#xff0c;扮演着无可替代的角色。然而&#xff0c;我们常常忽视的是&#xff0c;这些高架构筑物也存在着潜在的安全隐患&#xff0c;可能随时影响着交通的畅通和人们的生命财产安全。为了更好地认识和理解这…

基于百度文心大模型创作的实践与谈论

文心概念 百度文心大模型源于产业、服务于产业&#xff0c;是产业级知识增强大模型。百度通过大模型与国产深度学习框架融合发展&#xff0c;打造了自主创新的AI底座&#xff0c;大幅降低了AI开发和应用的门槛&#xff0c;满足真实场景中的应用需求&#xff0c;真正发挥大模型…

chatglm llm实时流api接口及post访问

参考&#xff1a; https://github.com/THUDM/ChatGLM-6B/pull/573/commits/02947052eefe392fd9f9632894e9551a805c6109 https://github.com/THUDM/ChatGLM-6B/pull/573 1、代码&#xff1a; 提前安装&#xff1a; sse_starlette、fastapi python stream_api.pystream_api.p…

Linux:如何挂载Window的共享目录

本文介绍的方法操作简单快捷&#xff0c;实用性强。下面就让小编来带大家学习“Linux下怎么挂载Window中的共享目录”吧! 一、在Window下创建共享目录 1、首先&#xff0c;在Window下创建一个目录作为共享目录&#xff0c;此处创建的目录名为ShareDir 2、右键目录&#xff0c…