图论:721. 账户合并(并查集扩展)

news2025/1/12 20:39:41

文章目录

  • 1、题目链接
  • 2、题目描述
  • 3、并查集思路
    • 3.1、按秩合并
    • 3.2、常用并查集代码
  • 4、题目解析

1、题目链接

721. 账户合并

2、题目描述

在这里插入图片描述在这里插入图片描述

3、并查集思路

并查集可以在很短的时间内合并不同的集合。它的思想为,一开始将不同单元单独作为一个结点,然后按等价条件进行合并,这个合并比单独使用集合合并快很多(因为单独使用集合这个数据结构,集合不能被真正合并,而且你需要快速找到是哪个集合,合并速度也没有并查集快)。我们一般使用整数下标作为一个结点。因此在涉及到字符串或者其他非整数个体时,我们可以使用哈希表将其映射到整数。

3.1、按秩合并

注意按秩合并不是按高度合并,因为高度很难维护。因为当路径压缩时高度减小,不能维护。
是指节点个数。
在这里插入图片描述
在这里插入图片描述

3.2、常用并查集代码

class UnionFind{
public:
    UnionFind(int n){
        for(int i = 0;i < n; ++ i){
            fa.emplace_back(i);
            rank.emplace_back(1);
        }
    }
    int Find(int x){
        if(fa[x] == -1) return -1;
        return fa[x] == x ? x : (fa[x] = Find(fa[x]));
    }
    void Union(int x, int y){
        int fax = Find(x);
        int fay = Find(y);
        if(fax != fay){
            if(rank[fax] > rank[fay]){
                fa[fay] = fax;
                rank[fax] ++;
            }else{
                fa[fax] = fay;
                rank[fay] ++;
            }
        }
        return;
    }
private:
    vector<int> fa;
    vector<int> rank;
};

4、题目解析

在这里插入图片描述

我们使用哈希表将邮箱编号,之后按账户邮箱进行并查集合并,我们知道一个账户的邮箱是只能属于同一个用户,这些属于同一个用户的邮箱是完全等价的。
比如:
["John", "johnsmith@mail.com", "john00@mail.com"], ["John", "johnsmith@mail.com", "john_newyork@mail.com"]
"johnsmith@mail.com""john00@mail.com"属于同一个用户
"johnsmith@mail.com""john_newyork@mail.com"属于同一个用户
则他们三共同属于一个用户,它们可以当作一个等价类,这个等价关系本质上是通过用户来确定的。

当我们将这些邮箱合并后,由于还是整数集合,我们再将其转化为字符串集合即可。再通过账户来看它的邮箱属于哪个等价类,来确定它的邮箱有哪些,确定过的账户,等价类标记为-1即可(等价类的父结点为-1),防止重复。

这里我采用了另一个哈希表,从父亲映射到一个有序的字符串等价类集合。这样我们对于一个账户通过其第一个邮箱就能找到其对应的等价类集合了。然后标记这个集合下次不再记入答案。

  1. 给字符串编号
  2. 将邮箱转成编号按用户合并
  3. 以父亲为代表求出其邮箱集合等价类(为了方便,使用set可以顺序访问,就不需要再排序了)
  4. 遍历账户取得其邮箱。
class UnionFind{
public:
    UnionFind(int n){
        for(int i = 0;i < n; ++ i){
            fa.emplace_back(i);
            rank.emplace_back(1);
        }
    }
    int Find(int x){
        if(fa[x] == -1) return -1;
        return fa[x] == x ? x : (fa[x] = Find(fa[x]));
    }
    void Union(int x, int y){
        int fax = Find(x);
        int fay = Find(y);
        if(fax != fay){
            if(rank[fax] > rank[fay]){
                fa[fay] = fax;
                rank[fax] ++;
            }else{
                fa[fax] = fay;
                rank[fay] ++;
            }
        }
        return;
    }
    void tag(int x){//标记并查集
        int fax = Find(x);
        fa[fax] = -1;
        return;
    }
private:
    vector<int> fa;
    vector<int> rank;
};
class Solution {
public:
    vector<vector<string>> accountsMerge(vector<vector<string>>& accounts) {
        int id = 0;
        //编号
        for(auto & emails : accounts){
            for(int i = 1; i < emails.size(); ++ i){
                if(mp.count(emails[i]) == 0){
                    mp[emails[i]] = id ++;//编号
                }
            }
        }
        //并查集合并邮箱
        UnionFind uf(id);
        for(auto & emails : accounts){
            int x = mp[emails[1]];//第一个邮箱
            for(int i = 2; i < emails.size(); ++ i){
                int y = mp[emails[i]];
                uf.Union(x, y);//合并两个邮箱
            }
        }
        //提取合并邮箱
        unordered_map<int, set<string>> email;
        for(auto & emails : accounts){
            for(int i = 1; i < emails.size(); ++ i){
                int y = uf.Find(mp[emails[i]]);
                set<string> & st = email[y];
                st.insert(emails[i]);
            }
        }
        //确定账户
        vector<vector<string>> ans;
        for(auto & emails : accounts){
            int x = mp[emails[1]];//第一个邮箱
            if(uf.Find(x) == -1) continue;
            vector<string> account;
            account.emplace_back(emails[0]);
            int fax = uf.Find(x);
            set<string> & st = email[fax];
            for(auto & t: st){
                account.emplace_back(t);
            }
            ans.emplace_back(account);
            uf.tag(x);//标记并查集为-1
        }
        return ans;
    }
private:
    unordered_map<string, int> mp;//字符串转为整数
};

这段代码使用并查集来合并邮箱账户,并输出合并后的结果。我们可以逐步解释这段代码的各个部分,以便更好地理解它的工作原理。

以下是代码详细解释:

代码概述

  1. UnionFind 类:实现并查集的数据结构,用于合并和查找元素。
  2. Solution 类:实现 accountsMerge 方法,用于合并邮箱账户。

UnionFind 类解释

  1. 构造函数:初始化 fa(父节点数组)和 rank(秩数组),每个元素的父节点初始化为自身,秩初始化为1。
  2. Find 方法:路径压缩,查找元素的根节点并将访问的所有节点直接连接到根节点。
  3. Union 方法:按秩合并,将秩较小的树合并到秩较大的树上。
  4. tag 方法:标记节点为 -1,表示该节点已经处理过。

Solution 类解释

  1. 编号阶段

    • 使用 mp 将每个邮箱字符串映射到一个唯一的整数ID。
    • 遍历 accounts,给每个邮箱分配一个唯一ID。
  2. 合并邮箱

    • 初始化 UnionFind 对象 uf,大小为所有邮箱的数量。
    • 遍历 accounts,对每个账户,将第一个邮箱和其他邮箱进行合并。
  3. 提取合并邮箱

    • 使用 email 哈希表将每个根ID映射到其对应的邮箱集合。
    • 遍历 accounts,将每个邮箱的根ID作为键,将邮箱加入到相应的集合中。
  4. 确定账户

    • 初始化结果向量 ans
    • 再次遍历 accounts,根据邮箱的根ID构建每个用户的账户信息,并将结果加入 ans
    • 使用 uf.tag(x) 标记已经处理的根ID,避免重复处理。

注意这里标记等价类(tag)也可以改为将集合清空,这样父亲对应的是一个空集合就说明这个集合已经处理过的,不过这样的时间复杂度高一点。

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

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

相关文章

【Qt】修改窗口的标题和图标

以下操作仅对顶层 widget(独⽴窗口),有效。 修改窗口的标题 一.windowTitle属性 1.概念 是一种在用户界面中显示窗口的标题的属性。它可以用来设置窗口的标题栏文本。 2.API API说明windowTitle()获取到控件的窗⼝标题.setWindowTitle(const QString& title)设置控件的…

线性回归和逻辑回归揭示数据的隐藏模式:理论与实践全解析

机器学习之线性回归和逻辑回归 1. 简介1.1 机器学习概述1.2 监督学习的定义与重要性1.3 线性回归和逻辑回归在监督学习中的作用1.3.1 线性回归1.3.2 逻辑回归 2. 线性回归&#xff08;Linear Regression&#xff09;2.1 定义与目标2.1.1 回归问题的定义2.1.2 预测连续目标变量 …

Redis持久化之RDB和AOF详解

持久化是确保 Redis 数据在服务器重启或崩溃时不丢失的关键功能。由于 Redis 是基于内存的数据库&#xff0c;如果不进行持久化&#xff0c;所有数据都存在于内存中&#xff0c;一旦服务器进程退出&#xff0c;内存中的数据就会丢失。持久化机制可以将 Redis 的数据库状态保存到…

Qt 学习第三天:加一个按钮

本章心得&#xff1a; 这个章节有点像写前端的味道了&#xff0c;设置按钮大小&#xff0c;按钮位置&#xff0c;窗口大小......代码全在widget.cpp上写的 #include "widget.h" #include "ui_widget.h" #include <QPushButton>Widget::Widget(QWid…

C++初级学习:⼊⻔基础

本文内容&#xff1a; 1.C参考⽂档&#xff1a;2.C第一个程序3.命名空间3.1namespace的价值3.2namespace的定义3.3命名空间的使用 4.C输⼊&输出5.缺省参数6.函数重载 1.C参考⽂档&#xff1a; https://legacy.cplusplus.com/reference/ https://zh.cppreference.com/w/cp…

实战:Zookeeper 简介和单点部署ZooKeeper

Zookeeper 简介 ZooKeeper是一个开源的分布式协调服务&#xff0c;它是Apache软件基金会下的一个项目&#xff0c;旨在解决分布式系统中的协调和管理问题。以下是ZooKeeper的详细简介&#xff1a; 一、基本定义 ZooKeeper是一个分布式的、开放源码的分布式应用程序协调服务&a…

添加索引导致微服务异常

一、现象 某app部分功能不可用&#xff0c;提示“连接服务器超时&#xff0c;请稍后尝试”。 二、分析 1、分析发现数据库存在大量的TM争用。 2、继续分析发现存在TM行锁的阻塞会话主要是以下几个&#xff1a; 3、查看其阻塞源头是1940 4、而1940进程在这个时间段是在跑新增索…

逆矩阵、秩

在数学的广阔天地中&#xff0c;线性代数扮演着至关重要的角色。它不仅是现代科学和工程学的基石&#xff0c;也是理解复杂数据结构的关键。本文将深入探讨线性代数中的几个核心概念&#xff1a;逆矩阵、秩、列空间和零空间&#xff0c;通过详细的解释和丰富的实例&#xff0c;…

(2024,LoRA压缩和多LoRA快速切换,联合对角化,重构误差)先压缩再提供服务:以极低的开销为数千个 LoRA提供服务

Compress then Serve: Serving Thousands of LoRA Adapters with Little Overhead 公和众与号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 1. 简介 3. 基于秩的 LoRA 压缩 3.1 期望特…

ubuntu安装tar安装 nginx最新版本

一、需要先安装依赖 apt install gcc libpcre3 libpcre3-dev zlib1g zlib1g-dev openssl libssl-dev 二、上传安装包 并解压 下载地址 nginx news tar xvf nginx-1.25.2.tar.gz 进入nginx cd nginx-1.25.2 三、编译 ./configure --prefix=/usr/local/nginx --with-htt…

数组模拟单调栈--C++

本题的计算量较大&#xff0c;用暴力算法会超时&#xff0c;的用别的方法&#xff0c;我们假设在左边找第一个比它小的数&#xff0c;那么在左边出现一次的数如果比右边大了&#xff0c;那么就不会在出现了&#xff0c;我们将它删除掉就可以了&#xff0c;用这个方法我们可以的…

算力QoS技术革新:OrionX引领AI行业资源管理新趋势

01 前言 在当今数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;已成为各行业发展的关键推动力。然而&#xff0c;随着AI技术的蓬勃发展&#xff0c;行业对计算资源的需求也日益增长&#xff0c;传统的资源分配方式已无法满足需求。 在这一背景下&#xff0c;算力Q…

基于vue-onlyoffice实现企业office web在线应用

目录 1.背景... 1 2.Onlyoffice介绍... 2 3.Onlyoffice核心api介绍... 2 3.1 ApiDocument 2 3.2 ApiParagraph. 2 3.3 ApiTable. 2 3.4. ApiRange. 3 4.Onlyoffice插件介绍... 3 4.1 插件定义... 3 4.2 插件对象... 3 4.3 插件结构... 4 4.4 插件内嵌使用方式... 4…

Echarts 柱状图实现同时显示百分比+原始值+汇总值

原始效果&#xff1a;柱状图 二开效果&#xff1a; 核心逻辑 同时显示百分比和原始值 label: {show: true,position: inside,formatter: (params) > {const rawValue rawData[params.seriesIndex][params.dataIndex];const percentage Math.round(params.value * 1000) / …

基于springboot+vue+uniapp的校园二手交易小程序

开发语言&#xff1a;Java框架&#xff1a;springbootuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#…

达梦数据库基础操作-查询

一、基础查询 1 &#xff09;单表查询 1. 查看表结构 使用两种方式可查看数据库的表结构&#xff1a; 查询后会显示该表的创建语句以及结构 2. 查询全表 使用 SELECT * 查询全表&#xff0c;此时数据库会返回表所有列 3. 行过滤 使用条件查询进行过滤&#xff0c;…

2024电赛H题参考方案(+视频演示+核心控制代码)——自动行使小车

目录 一、题目要求 二、参考资源获取 三、参考方案 1、环境搭建及工程移植 2、相关模块的移植 4、整体控制方案视频演示 5、视频演示部分核心代码 总结 一、题目要求 小编自认为&#xff1a;此次H题属于控制类题目&#xff0c;相较于往年较为简单&#xff0c;功能也算单一&…

Vue - CSS基础学习

一、元素及属性 CSS 是为 web 内容添加样式的代码。 style标签 1.语法 1.除了选择器部分&#xff0c;每个规则集都应该包含在成对的大括号里&#xff08;{}&#xff09;。 2.在每个声明里要用冒号&#xff08;:&#xff09;将属性与属性值分隔开。 3.在每个规则集里要用分号…

Windows执行jar包

配置环境变量&#xff1a; 命令行测试&#xff1a; java -version 将jar包上传至指定目录&#xff0c;在该目录下创建运行脚本&#xff1a; chcp 65001 java -Dfile.encodingutf-8 -jar jxpaddle-admin.jar chcp 65001&#xff1a;将当前cmd编码改为UTF-8&#xff0c;仅对当…

单片机芯片程序读取方法和工具

如何把单片机芯片程序读取出来 读取芯片中的程序可以通过多种方法实现&#xff0c;具体方法取决于芯片的类型和可用的工具。 一、使用‌Keil软件&#xff1a; 如果芯片是Flash类型的&#xff0c;可以使用Keil软件配合硬件调试工具进行读取。首先&#xff0c;需要配置Keil工程&…