20240223-2092.查找所有有秘密的人

题目要求

给你一个整数 n,表示有 n 个人,编号从 0 到 n - 1。你还给你一个 0 索引的二维整数数组 meetings,其中 meetings[i] = [xi, yi, timei] 表示 xi 和 yi 在 timei 有一个会议。一个人可以同时参加多个会议。最后,给你一个整数 firstPerson。

人 0 有一个秘密,并在时间 0 开始与人 firstPerson 共享该秘密。更正式地说,在每次会面时,如果某人 xi 在时间 i 拥有该秘密,那么他将与某人 yi 共享该秘密,反之亦然。

秘密是即时共享的。也就是说,一个人收到秘密后,可以在同一时间内与其他会议上的人分享。

在所有会议结束后,返回所有拥有该秘密的人的名单。您可以按照任何顺序返回答案。

思路

我们需要找到所有会议结束之后(时间最后)拥有信息的人的名单,那么我们就需要将会议按照时间进行排序。

第二我们需要在同一会议时间判断出谁拥有秘密,谁在当前时间获得了秘密。然后这个过程继续传递直到时间结束。

在0时刻,编号为0和firstPerson的人获得了秘密。然后在之后的每一个时间点我们需要找到0和firstPerson和其他哪些人产生了交集,而后以此类推。

为了完成这一目的,根据建议我们应该采取并查集(Union-Find)的数据结构。

并查集是一种非常高效的数据结构,用于处理一些不交集的合并及查询问题,特别适用于本题中的场景,即追踪和管理一组动态集合的合并与查找操作。

并查集主要支持两种操作:

  • Find: 确定某个元素属于哪个集合,也就是这个元素的“根”是什么。在并查集中,每个集合由一个代表元素(根)来标识,所有属于同一集合的元素的根都是相同的。
  • Union: 将两个元素所在的集合合并为一个集合。这通常通过将一个集合的根元素连接到另一个集合的根元素来完成。

并查集通过数组或哈希表来实现,每个元素都有一个指向父元素的指针,根元素的指针指向它自己,这样形成一个树状结构。

基本操作的实现

  1. 初始化(Initialization):一开始,每个人都在自己独立的集合中,也就是每个人都是自己集合的根。

  2. 查找(Find):为了找到元素的根(代表元素),我们不断地追溯它的父元素,直到找到一个指向自己的元素,那就是根。在实际操作中,为了提高效率,我们会进行路径压缩,即在执行查找操作的同时,将查找路径上的所有元素直接连接到根上,这样可以减少后续查找操作的时间。

  3. 合并(Union):当我们想要合并两个元素所在的集合时,我们首先找到它们各自的根,如果根不同,就将其中一个根的父元素设置为另一个根,从而将两个树合并为一棵。为了保持树的平衡,通常会使用按秩合并,即总是将较小的树连接到较大的树上。

代码

#include <algorithm>
#include <unordered_set>
class UnionFind {
public:vector<int> parent;vector<int> rank;UnionFind(int size) : parent(size), rank(size, 0) {for (int i = 0; i < size; ++i) {parent[i] = i;}}int find(int x) {if (x != parent[x]) {parent[x] = find(parent[x]);}return parent[x];}void unionSet(int x, int y) {int rootX = find(x);int rootY = find(y);if (rootX != rootY) {if (rank[rootX] < rank[rootY]) {parent[rootX] = rootY;} else if (rank[rootX] > rank[rootY]) {parent[rootY] = rootX;} else {parent[rootY] = rootX;rank[rootX]++;}}}
};
class Solution {
public:vector<int> findAllPeople(int n, vector<vector<int>>& meetings, int firstPerson) {UnionFind uf(n);uf.unionSet(0, firstPerson);sort(meetings.begin(), meetings.end(), [](const vector<int>& a, const vector<int>& b) {return a[2] < b[2];});vector<int> secretHolders;for (int i = 0; i < meetings.size(); ) {int time = meetings[i][2];unordered_set<int> thisTimeMeeting;while (i < meetings.size() && meetings[i][2] == time) {uf.unionSet(meetings[i][0], meetings[i][1]);thisTimeMeeting.insert(meetings[i][0]);thisTimeMeeting.insert(meetings[i][1]);i++;}for (int person : thisTimeMeeting) {if (uf.find(person) != uf.find(0)) {uf.parent[person] = person;}}}for (int i = 0; i < n; ++i) {if (uf.find(i) == uf.find(0)) {secretHolders.push_back(i);}}return secretHolders;}
};

时空复杂度

时间复杂度

空间复杂度

理解并查集

理解并查集的这种写法,我们可以分解为几个关键点:初始化、查找(Find)、合并(Union)以及路径压缩的实现。这些是并查集数据结构核心操作的实现,旨在高效处理动态连通性问题。

初始化(Constructor)

在并查集的构造函数中,每个元素最开始都指向自己,表示每个元素自成一个集合。rank数组用于记录每个根节点所在树的深度,有助于在执行合并操作时保持树的平衡,减少查找时间。

UnionFind(int size) : parent(size), rank(size, 0) {for(int i = 0; i < size; ++i) {parent[i] = i; // 每个节点的父节点指向自己}
}

查找(Find)

查找操作用于找到给定元素所在集合的根节点(代表元素)。路径压缩技术通过将查找路径上的每个节点直接链接到根节点,从而降低后续查找操作的复杂度。

int find(int x) {if(x != parent[x]) {parent[x] = find(parent[x]); // 路径压缩}return parent[x];
}

合并(Union)

合并操作用于将两个元素所在的集合合并为一个集合。它首先找到两个元素各自的根节点,如果根节点不同,说明它们属于不同的集合,需要合并。使用rank来决定如何合并,以保持树的平衡性,避免形成深度过大的树结构。

void unionSet(int x, int y) {int rootX = find(x);int rootY = find(y);if(rootX != rootY) {if(rank[rootX] < rank[rootY]) {parent[rootX] = rootY; // 将较浅的树的根节点指向较深的树的根节点} else if(rank[rootX] > rank[rootY]) {parent[rootY] = rootX;} else {parent[rootY] = rootX; // 如果深度相同,就选择一个作为根,增加其深度rank[rootX]++;}}
}

理解关键

  • 目的:并查集是为了高效地解决动态连通性问题设计的,它可以快速判断网络中任意两个点是否属于同一个连通分量,以及将两个连通分量合并。
  • 路径压缩:查找操作中的路径压缩是并查集高效的关键所在。通过将查找路径上的节点直接连接到根节点,可以显著降低后续查找的复杂度。
  • 按秩合并:通过比较两棵树的深度(秩)来决定合并方向,可以避免形成深度过大的树,从而保持操作的高效性。

通过这样的实现,我们可以非常高效地处理大量元素的动态联通性问题,这对于解决诸如网络连通性、集合合并等问题至关重要。

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

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

相关文章

51单片机学习(5)-----蜂鸣器的介绍与使用

前言&#xff1a;感谢您的关注哦&#xff0c;我会持续更新编程相关知识&#xff0c;愿您在这里有所收获。如果有任何问题&#xff0c;欢迎沟通交流&#xff01;期待与您在学习编程的道路上共同进步。 目录 一. 蜂鸣器的介绍 1.蜂鸣器介绍 2.压电式蜂鸣器 &#xff08;无源…

前端网页位置

网页可见区域高&#xff1a;document.body.clientHeight&#xff08;不包括边线的高&#xff09; 网页可见区域高&#xff1a;document.body.offsetHeight&#xff08;包括边线的高&#xff09; 网页正文全文高&#xff1a;document.body.scrollHeight 网页被卷去的高度&#x…

【人脸朝向识别与分类预测】基于BP神经网络

课题名称&#xff1a;基于BP神经网络的人脸朝向识别分类 版本日期&#xff1a;2024-02-20 运行方式&#xff1a;直接运行BP0503.m文件 代码获取方式&#xff1a;私信博主或 QQ:491052175 模型描述&#xff1a; 采集到一组人脸朝向不同角度时的图像&#xff0c;图像来自不同…

springboot集成quartz定时任务并接入后台管理系统(copy即用)

说明:项目启动后会根据设置的时间进行执行,业务代码根据自己的需求更改,数据库文件在最后(记得清空数据库哦~)这里需要注意的一点就是className字段表示的是下面的对应的DynamicTask的路径如:com.example.demo.quartz.task.DynamicTask,如有多个定时任务copy并更改Dynam…

成都源聚达:开抖音店铺的成本用得了多少

在数字浪潮中&#xff0c;抖音不仅是年轻人的娱乐天地&#xff0c;也成为了新兴电商平台。不少创业者摩拳擦掌&#xff0c;想要在此开疆拓土。然而&#xff0c;开店并非空谈梦想&#xff0c;成本的投入是实现梦想的基石。那么&#xff0c;开设一家抖音店铺究竟需要多少成本呢?…

C++之std::async

std::async是C提供的一个异步处理函数。 函数原型&#xff1a; template<typename _Fn, typename... _Args> future<__async_result_of<_Fn, _Args...>> async(launch __policy, _Fn&& __fn, _Args&&... __args); 参数说明: int thFun(in…

李亚飞:什么是开发人员的工程能力?如何考察?

可以说工程能力是软件工程师最核心的能力&#xff0c;工程能力强的人工作效率往往很高&#xff0c;在动手之前就想清楚更多研发风险&#xff0c;也可以提出更多产品意见。 但到底什么是工程能力&#xff0c;该如何考察&#xff0c;是本文想跟大家探讨的内容。 知乎上关于【工…

如何使用GAP-Burp-Extension扫描潜在的参数和节点

关于GAP-Burp-Extension GAP-Burp-Extension是一款功能强大的Burp扩展&#xff0c;该工具在getAllParams扩展的基础上进行了升级&#xff0c;该工具不仅可以帮助广大研究人员在安全审计过程中扫描潜在的参数&#xff0c;而且还可以搜索潜在的链接并使用这些参数进行测试&#…

基于Prony算法的系统参数辨识matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 Prony算法是一种用于信号处理和系统辨识的经典方法&#xff0c;特别适用于线性时不变系统&#xff08;LTI&#xff09;的频率响应分析以及模拟复指数信号序列。其…

异地组网什么原理?企业适合SDWAN异地组网吗?

深入解析异地组网及其对企业的影响 在数字化时代的洪流中&#xff0c;企业正经历着前所未有的变革。随着业务需求的多样化和全球化&#xff0c;传统的网络架构已无法满足现代企业的灵活性和效率要求。异地组网技术的兴起&#xff0c;特别是SD-WAN的应用&#xff0c;为企业提供…

【每周AI简讯】Stable Diffusion 3大版本更新

ChatGPT中文版AI7号 Stable Diffusion 3大版本更新 Stability AI发布了其最新的图像生成模型Stable Diffusion 3&#xff0c;旨在挑战Sora和Gemini。此版本采用创新架构&#xff0c;提高跨硬件系统的性能&#xff0c;需较大计算力。Stable Diffusion 3增加了“流匹配”技术&a…

SQL-Labs靶场“26-28”关通关教程

君衍. 一、二十六关 基于GET过滤空格以及注释报错注入1、源码分析2、绕过思路3、updatexml报错注入 二、二十六a关 基于GET过滤空格注释字符型注入1、源码分析2、绕过思路3、时间盲注 三、二十七关 基于union及select的过滤单引号注入1、源码分析2、绕过思路3、联合查询注入4、…

Java设计模式 | 七大原则之依赖倒转原则

依赖倒转原则&#xff08;Dependence Inversion Principle&#xff09; 基本介绍 高层模块不应该依赖低层模块&#xff0c;二者都应该依赖其抽象&#xff08;接口/抽象类&#xff09;抽象不应该依赖细节&#xff0c;细节应该依赖抽象依赖倒转&#xff08;倒置&#xff09;的…

ZYNQ Vivado更新硬件后SDK不更新问题解决办法

一、情况说明 软件版本 Vivado 2018.3 Vivado更新硬件导出后&#xff0c;按正常SDK会自动检测到hdf文件的变化跳出更新提示&#xff08;如下图所示&#xff09;。但是我的项目如果是复制的或者是长时间没打开的项目更新硬件配置导出后SDK无法自动更新。 二、解决办法 2.1 …

苏宁商品详情大揭秘:一键解锁API接口,电商数据尽在掌握

苏宁商品详情API接口技术深度探索 一、引言 在电商领域&#xff0c;获取商品详情是许多业务场景的基础需求。苏宁商品详情API接口为此提供了便捷的途径。本文将带你深入了解苏宁商品详情API接口的技术细节&#xff0c;帮助你更好地利用这一接口&#xff0c;提升业务效率。 二…

刷题日记 | 字符串扩容和增强型for循环

for(char c:s)遍历字符串 增强型for循环 C for(char c:s)遍历字符串 增强型for循环_c for (char c : s)-CSDN博客 字符串使用前要进行扩容 reserve函数 【CString类成员函数辨析】resize(),size(),capacity(),reserve()函数的解析与对比_c reserve函数-CSDN博客 a.size() 用来…

【已解决】解决Win11忘记开机密码(不用重装系统)

问题起因 因为在实验室的电脑从过年就没有用过&#xff0c;也不知道为什么记性这么差&#xff0c;就把电脑密码忘了&#xff0c;但是又不想用系统盘重装电脑。于是从网上整理一些文章&#xff0c;最后写了下面一篇解决方法 解决方法 1.首先在登录界面&#xff08;输入密码那…

leetcode:46.全排列

1.什么是排列&#xff1f; 有顺序&#xff01;&#xff01; 2.树形结构&#xff1a; 使用used数组进行标记取过的元素&#xff0c;一个元素一个元素地进行取值&#xff0c;取完之后将used数组进行标记。 3.代码实现&#xff1a;&#xff08;循环从i0开始&#xff0c;而不是…

转本考前如何调整心态

不少同学还在过年的氛围中还没走出来。 担忧自己成绩不进反退&#xff0c;又不知道该如何调整心态&#xff01;这个时候小编就有几点小建议给到各位考生。 *心态*情绪 良好的考试心态是没有固定的心态&#xff0c;对不同学习情况的学生来说&#xff0c;良好的考试心态是不一…

如何优化一个看似正常的数据库

通常DBA是不会太了解业务逻辑的&#xff0c;遇到系统中劣质的sql 一般也是以通过添加索引的方式来优化&#xff0c;但是并不是所有的sql都能通过添加索引来优化 这就需要重sql的本身来做分析&#xff0c;另外还要了解什么样的语句会不走索引&#xff01;本文通过几个简单的例子…