【并集查找 图论】2421. 好路径的数目

本文涉及知识点

C++图论

LeetCode2421. 好路径的数目

给你一棵 n 个节点的树(连通无向无环的图),节点编号从 0 到 n - 1 且恰好有 n - 1 条边。
给你一个长度为 n 下标从 0 开始的整数数组 vals ,分别表示每个节点的值。同时给你一个二维整数数组 edges ,其中 edges[i] = [ai, bi] 表示节点 ai 和 bi 之间有一条 无向 边。
一条 好路径 需要满足以下条件:
开始节点和结束节点的值 相同 。
开始节点和结束节点中间的所有节点值都 小于等于 开始节点的值(也就是说开始节点的值应该是路径上所有节点的最大值)。
请你返回不同好路径的数目。
注意,一条路径和它反向的路径算作 同一 路径。比方说, 0 -> 1 与 1 -> 0 视为同一条路径。单个节点也视为一条合法路径。
示例 1:
在这里插入图片描述

输入:vals = [1,3,2,1,3], edges = [[0,1],[0,2],[2,3],[2,4]]
输出:6
解释:总共有 5 条单个节点的好路径。
还有 1 条好路径:1 -> 0 -> 2 -> 4 。
(反方向的路径 4 -> 2 -> 0 -> 1 视为跟 1 -> 0 -> 2 -> 4 一样的路径)
注意 0 -> 2 -> 3 不是一条好路径,因为 vals[2] > vals[0] 。
示例 2:
在这里插入图片描述

输入:vals = [1,1,2,2,3], edges = [[0,1],[1,2],[2,3],[2,4]]
输出:7
解释:总共有 5 条单个节点的好路径。
还有 2 条好路径:0 -> 1 和 2 -> 3 。
示例 3:
在这里插入图片描述

输入:vals = [1], edges = []
输出:1
解释:这棵树只有一个节点,所以只有一条好路径。
提示:
n == vals.length
1 <= n <= 3 * 104
0 <= vals[i] <= 105
edges.length == n - 1
edges[i].length == 2
0 <= ai, bi < n
ai != bi
edges 表示一棵合法的树。

并集查找(并差集)

vNode[i]记录值为i的节点。
vEdge[i]记录边的两个端点的最大值为i的边。
通过i从小到大处理值。
一,连接vEdge[i]的各边。
二,计算vNode[i]各节点所在连通区域的包括值为i的节点数量。
三,令某个连通区域值为i的节点数量为x。则数量加x × \times ×(x-1)/2。
最终答案需要增加单节点的数量n。
m = edges.length
时间复杂度😮(n+m)
边和节点都只会处理一次。

代码

class CUnionFind
{
public:CUnionFind(int iSize) :m_vNodeToRegion(iSize){for (int i = 0; i < iSize; i++){m_vNodeToRegion[i] = i;}m_iConnetRegionCount = iSize;}	CUnionFind(vector<vector<int>>& vNeiBo):CUnionFind(vNeiBo.size()){for (int i = 0; i < vNeiBo.size(); i++) {for (const auto& n : vNeiBo[i]) {Union(i, n);}}}int GetConnectRegionIndex(int iNode){int& iConnectNO = m_vNodeToRegion[iNode];if (iNode == iConnectNO){return iNode;}return iConnectNO = GetConnectRegionIndex(iConnectNO);}void Union(int iNode1, int iNode2){const int iConnectNO1 = GetConnectRegionIndex(iNode1);const int iConnectNO2 = GetConnectRegionIndex(iNode2);if (iConnectNO1 == iConnectNO2){return;}m_iConnetRegionCount--;if (iConnectNO1 > iConnectNO2){UnionConnect(iConnectNO1, iConnectNO2);}else{UnionConnect(iConnectNO2, iConnectNO1);}}bool IsConnect(int iNode1, int iNode2){return GetConnectRegionIndex(iNode1) == GetConnectRegionIndex(iNode2);}int GetConnetRegionCount()const{return m_iConnetRegionCount;}vector<int> GetNodeCountOfRegion()//各联通区域的节点数量{const int iNodeSize = m_vNodeToRegion.size();vector<int> vRet(iNodeSize);for (int i = 0; i < iNodeSize; i++){vRet[GetConnectRegionIndex(i)]++;}return vRet;}std::unordered_map<int, vector<int>> GetNodeOfRegion(){std::unordered_map<int, vector<int>> ret;const int iNodeSize = m_vNodeToRegion.size();for (int i = 0; i < iNodeSize; i++){ret[GetConnectRegionIndex(i)].emplace_back(i);}return ret;}
private:void UnionConnect(int iFrom, int iTo){m_vNodeToRegion[iFrom] = iTo;}vector<int> m_vNodeToRegion;//各点所在联通区域的索引,本联通区域任意一点的索引,为了增加可理解性,用最小索引int m_iConnetRegionCount;
};class Solution {
public:int numberOfGoodPaths(vector<int>& vals, vector<vector<int>>& edges) {const int iMax = *max_element(vals.begin(), vals.end());vector<vector<int>> vNoedes(iMax+1);vector<vector<pair<int, int>>> vEdges(iMax + 1);for (int i = 0; i < vals.size(); i++) {vNoedes[vals[i]].emplace_back(i);}for (const auto& v : edges) {const int m = max(vals[v[0]], vals[v[1]]);vEdges[m].emplace_back(v[0], v[1]);}CUnionFind uf(vals.size());int ret = 0;for (int i = 0; i <= iMax; i++) {for (const auto& [n1, n2] : vEdges[i]) {uf.Union(n1, n2);}unordered_map<int, int> cnt;for (int n : vNoedes[i]) {cnt[uf.GetConnectRegionIndex(n)]++;}for (const auto& [tmp, c] : cnt) {ret += c * (c - 1) / 2;}}return ret + vals.size();}
};

单元测试

template<class T1, class T2>
void AssertEx(const T1& t1, const T2& t2)
{Assert::AreEqual(t1, t2);
}
void AssertEx( double t1,  double t2)
{auto str = std::to_wstring(t1) + std::wstring(1,32) + std::to_wstring(t2);Assert::IsTrue(abs(t1 - t2) < 1e-5,str.c_str() );
}template<class T>
void AssertEx(const vector<T>& v1, const vector<T>& v2)
{Assert::AreEqual(v1.size(), v2.size());for (int i = 0; i < v1.size(); i++){Assert::AreEqual(v1[i], v2[i]);}
}template<class T>
void AssertV2(vector<vector<T>> vv1, vector<vector<T>> vv2)
{sort(vv1.begin(), vv1.end());sort(vv2.begin(), vv2.end());Assert::AreEqual(vv1.size(), vv2.size());for (int i = 0; i < vv1.size(); i++){AssertEx(vv1[i], vv2[i]);}
}namespace UnitTest
{vector<int> vals;vector<vector<int>> edges;TEST_CLASS(UnitTest){public:TEST_METHOD(TestMethod00){vals = { 1,3,2,1,3 }, edges = { {0,1},{0,2},{2,3},{2,4} };auto res = Solution().numberOfGoodPaths(vals, edges);AssertEx(6, res);}TEST_METHOD(TestMethod01){vals = { 1,1,2,2,3 }, edges = { {0,1},{1,2},{2,3},{2,4} };auto res = Solution().numberOfGoodPaths(vals, edges);AssertEx(7, res);}TEST_METHOD(TestMethod02){vals = { 1 }, edges = {};auto res = Solution().numberOfGoodPaths(vals, edges);AssertEx(1, res);}TEST_METHOD(TestMethod03){vals = { 2,5,5,1,5,2,3,5,1,5 }, edges = { {0,1},{2,1},{3,2},{3,4},{3,5},{5,6},{1,7},{8,4},{9,7} };auto res = Solution().numberOfGoodPaths(vals, edges);AssertEx(20, res);}};
}

扩展阅读

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关推荐

我想对大家说的话
《喜缺全书算法册》以原理、正确性证明、总结为主。
按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++11及其特性】函数返回值当引用

函数返回值当引用目录 一.若返回变量为栈变量1.例子2.不能成为其他引用的初始值3.不能作为左值 二.若返回变量为静态变量或全局变量1.列子2.即可左值也可右值 三.若返回变量为形参1.普通形参2.引用形参 四.结论 一.若返回变量为栈变量 1.例子 返回的是局部变量的引用,这里用的…

【Python系列】SQLAlchemy 基本介绍

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

5.3二叉树——二叉树链式结构实现

本篇博客梳理二叉树链式结构 明确&#xff1a;二叉树是递归定义的 递归的本质&#xff1a;当前问题子问题&#xff0c;返回条件是最小规模的子问题 一、二叉树的遍历 1&#xff0e;前序、中序与后序遍历 &#xff08;1&#xff09;前序&#xff1a;根->左子树->右子树…

书生大模型实战营(1)——InterStudio基础知识+Vscode SSH连接远程服务器+Linux基础指令

参加书生.浦江大模型实战训练营&#xff0c;学习大模型知识和微调技术&#xff0c;所有课程免费&#xff0c;通过闯关的形式学习&#xff0c;也比较有趣。一起来了解LLM的世界。邀请链接 产品简介 InternStudio 是大模型时代下的云端算力平台。基于 InternLM 组织下的诸多算法…

经典文献阅读之--ParkingE2E(基于摄像头的端到端停车网络:从图像到规划)

0. 简介 自动泊车是智能驾驶领域的一项关键任务。传统泊车算法通常采用基于规则的方案来实现。然而&#xff0c;由于算法设计的复杂性&#xff0c;这些方法在复杂的泊车场景中效果欠佳。相比之下&#xff0c;基于神经网络的方法往往比基于规则的方法更加直观且功能多样。通过收…

ORACLE 统计信息的备份与恢复

备份 --需要先创建统计信息基础表 exec dbms_stats.create_stat_table(USER1,STAT_TIMESTAMP); --导出某个用户的所有统计信息 exec dbms_stats.export_schema_stats(USER1,STAT_TIMESTAMP);--测试(插入100条&#xff0c;更新统计信息&#xff0c;略) select num_rows,last_ana…

基于STM32开发的简易自动驾驶系统

目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 系统初始化传感器数据采集与处理电机控制与转向OLED显示与状态提示Wi-Fi通信与远程监控应用场景 简易自动驾驶演示智能车模型开发与学习常见问题及解决方案 常见问题解决方案结论 1. 引言 随…

蜂鸣器奏乐

一、粗略了解简谱 拍号&#xff1a;如图&#xff0c;“2”表示一个小节有2拍&#xff0c;“4”表示4分音符为一拍 终止线表示歌曲结束 注意&#xff1a;以下音符都按以四分音符为一拍计算拍数 四分音符&#xff1a; 唱一拍 二分音符&#xff1a; 某一个音右边有一个小横线&…

中国招标投标平台JS逆向:DES加密与Python纯算还原

中国招标投标平台JS逆向&#xff1a;DES加密与Python纯算还原 目录 &#x1f510; JS DES解密&#x1f9ee; Python版本的纯算实现 &#x1f510; JS DES解密 在中国招标投标公共服务平台的分析过程中&#xff0c;发现了数据加密采用了DES算法。DES&#xff08;数据加密标准&…

github源码指引:C++嵌入式WEB服务器

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 相关专题&#xff1a; C嵌入式…

Broker服务器模块

一.Broker模块介绍 二.Broker模块具体实现 1. 类的成员变量与构造函数 成员变量 事件循环和TCP服务器: muduo::net::EventLoop _baseloop;muduo::net::TcpServer _server; 这些是muduo库提供的核心组件&#xff0c;负责处理网络事件和管理TCP连接。 消息分发和编码: muduo::…

Spring Security 认证源码超详细分析

Spring Security 认证源码超详细分析 认证&#xff08;Authentication&#xff09;是系统确认用户信息的重要途径&#xff0c;用户通过认证之后&#xff0c;系统才能明确用户的身份&#xff0c;进而才可以为该用户分配一定的权限&#xff0c;这个过程也叫授权&#xff08;Auth…

项目实战--Sa-Token详细方案笔记

Sa-Token权限系统设计 一、前言二、认证授权的概念三、Sa-Token简介3.1 Sa-Token使用方式3.2 踢人下线3.3 全局异常处理3.4 二级认证3.5 同端互斥登录3.6 Http Basic/Digest 认证3.6.1 HttpBasic认证3.6.2 Http Digest 认证 四、Sa-Token授权&#xff08;鉴权&#xff09;4.1 权…

详说 类和对象

类怎么定义 类是什么呢&#xff1f;类就是我们上篇文说的命名空间&#xff0c;单独创建一个域&#xff0c;自己有自己的生命空间&#xff0c;那么类怎么定义呢&#xff1f;C规定&#xff0c;假设 stack就是他的类名&#xff0c;那么前面要加个class&#xff0c;换行之后就是他…

汽车乘客热舒适度大挑战,如何利用仿真技术提高汽车环境舒适度

舒适性在人们选择汽车的决定性方面占比越来越重&#xff0c;而汽车乘员舱环境的舒适性是指为乘员提供舒适愉快便利的乘坐环境与条件&#xff0c;包括良好的平顺性、车内的低噪声、适宜的空气环境以及良好的驾驶操作性能。 舒适性 经济性 安全性、动力性 典型的乘员舱热舒适性模…

laravel的队列的使用

laravel队列 laravel的特性&#xff1a;laravel队列可以基于不同的后台存储服务提供统一的api&#xff0c;后台存储服务包括 Redis MySQL等。队列实现了业务解耦&#xff0c;异步处理&#xff0c;错误重试的功能。比如调用第三方api&#xff0c;无法保证api的可靠性&#xff0…

Transformer 与传统模型Informer

Transformer 与传统模型:Informer 如何改变时间序列预测的规则 Transformers 是那些聪明的注意力构建者,它们在机器学习的各个领域掀起了波澜。但在时间序列预测领域,它们才真正大显身手。你可能会问,为什么?想象一下,有一个水晶球,它不仅能看到未来,还能理解导致未来的…

TCP协议 配合 Wireshark 分析数据

在TCP连接中&#xff0c;无论是客户端还是服务端&#xff0c;都有可能成为发送端或接收端&#xff0c;这是因为TCP是一个全双工协议&#xff0c;允许数据在同一连接中双向流动 客户端&#xff08;Client&#xff09;&#xff1a;通常是指主动发起连接请求的一方。例如&#xf…

宠物空气净化器有用吗?为什么养宠家庭要买宠物空气净化器?

身为一个鼻炎患者&#xff0c;却喜欢猫咪&#xff0c;所以毅然决然的养了两只宠物&#xff0c;而且还是长毛猫&#xff0c;不要问为什么鼻炎还买两只猫咪&#xff0c;因为怕一只猫咪孤单&#xff0c;所以养了两只。对于很多人来说&#xff0c;猫咪就像焦虑不安时的精神搭子&…

如何让私域服务赢得用户的心?

私域流量的概念在当今的商业环境中已经变得极为重要&#xff0c;许多品牌和企业都投入大量资源尝试通过各种策略吸引并保留用户。然而&#xff0c;单纯的流量积累并不足以确保商业成功。当面对用户的沉默、缺乏活跃度以及无法变现的困境时&#xff0c;我们必须重新审视私域流量…