“微信群覆盖”,线性求解方案?

2023-11-30 14:58

本文主要是介绍“微信群覆盖”,线性求解方案?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目:求微信群覆盖

微信有很多群,现进行如下抽象:

(1) 每个微信群由一个唯一的gid标识;

(2) 微信群内每个用户由一个唯一的uid标识;

(3) 一个用户可以加入多个群;

(4) 群可以抽象成一个由不重复uid组成的集合,例如:

g1{u1, u2, u3}

g2{u1, u4, u5}

可以看到,用户u1加入了g1与g2两个群。

画外音,注意:

gid和uid都是uint64;

集合内没有重复元素;

假设微信有M个群(M为亿级别),每个群内平均有N个用户(N为十级别).

现在要进行如下操作:

(1)  如果两个微信群中有相同的用户,则将两个微信群合并,并生成一个新微信群;

例如,上面的g1和g2就会合并成新的群:

g3{u1, u2, u3, u4, u5};

画外音:集合g1中包含u1,集合g2中包含u1,合并后的微信群g3也只包含一个u1。

(2) 不断的进行上述操作,直到剩下所有的微信群都不含相同的用户为止;

将上述操作称:求群的覆盖。

设计算法,求群的覆盖,并说明算法时间与空间复杂度。

画外音:58同城2013年校招笔试题。

《暴力法求解“微信群覆盖”》使用了暴力法,循环遍历所有的集合对,合并存在公共元素的集合对,暴力求解。

 

《染色法求解“微信群覆盖”》使用了染色法,通过以下几个步骤,加快了求解速度:

(1) 全部元素全局排序;

(2) 全局排序后,不同集合中的相同元素,一定是相邻的,通过相同相邻的元素,一次性找到所有需要合并的集合;

(3) 合并这些集合,算法完成;

 

同时文章遗留了两个问题:

步骤(2)中,如何通过元素快速定位集合?

步骤(3)中,如何快速合并集合?

今天,将要讲讲这两个问题的优化思路。

 

问题一:如何由元素快速定位集合? 

普通的集合,只能由集合根(root)定位元素,不能由元素逆向定位root,如何支持元素逆向定位root呢?

很容易想到,每个节点增加一个父指针即可。

 

更具体的:

element{

         int data;

         element* left;

         element* right;

}

 

升级为:

element{

         element* parent;    // 指向父节点

         int data;

         element* left;

         element* right;

}

 

如上图:所有节点的parent都指向它的上级,而只有root->parent=NULL。

 

对于任意一个元素,找root的过程为:

element* X_find_set_root(element* x){

         element* temp=x;

         while(temp->parent != NULL){

                   temp= temp->parent;

         }

         return temp;

}

 

很容易发现,由元素找集合根的时间复杂度是树的高度,即O(lg(n))。

 

有没有更快的方法呢?

进一步思考,为什么每个节点要指向父节点,直接指向根节点是不是也可以。

 

更具体的:

element{

         int data;

         element* left;

         element* right;

}

 

升级为:

element{

         element* root;         // 指向集合根

         int data;

         element* left;

         element* right;

}

 

如上图:所有节点的parent都指向集合的根。

 

对于任意一个元素,找root的过程为:

element* X_find_set_root(element* x){

         return x->root;

}

 

很容易发现,升级后,由元素找集合根的时间复杂度是O(1)。

画外音:不能更快了吧。

 

另外,这种方式,能在O(1)的时间内,判断两个元素是否在同一个集合内:

bool in_the_same_set(element* a, element* b){

         return (a->root == b->root);

}

甚为方便。

 

问题二:如何快速进行集合合并? 

《暴力法求解“微信群覆盖”》一文中提到过,集合合并的伪代码为:

merge(set(i), set(j)){

         foreach(element in set(i))

                   set(j).insert(element);

}

把一个集合中的元素插入到另一个集合中即可。

 

假设set(i)的元素个数为n1,set(j)的元素个数为n2,其时间复杂度为O(n1*lg(n2))。

 

在“微信群覆盖”这个业务场景下,随着集合的不断合并,集合高度越来越高,合并会越来越慢,有没有更快的集合合并方式呢?

 

仔细回顾一下:

  • 树形set的优点是,支持有序查找,省空间

  • 哈希型set的优点是,快速插入与查找

 

而“微信群覆盖”场景对集合的频繁操作是:

  • 由元素找集合根

  • 集合合并

 

那么,为什么要用树形结构或者哈希型结构来表示集合呢?

画外音:优点完全没有利用上嘛。

 

让我们来看看,这个场景中,如果用链表来表示集合会怎么样,合并会不会更快?

s1={7,3,1,4}

s2={1,6}

如上图,分别用链表来表示这两个集合。可以看到,为了满足“快速由元素定位集合根”的需求,每个元素仍然会指向根。

 

s1和s2如果要合并,需要做两件事:

(1) 集合1的尾巴,链向集合2的头(蓝线1);

(2) 集合2的所有元素,指向集合1的根(蓝线2,3);

 

合并完的效果是:

变成了一个更大的集合。

 

假设set(1)的元素个数为n1,set(2)的元素个数为n2,整个合并的过程的时间复杂度是O(n2)。

画外音:时间耗在set(2)中的元素变化。

 

咦,我们发现:

  • 将短的链表,接到长的链表上

  • 将长的链表,接到短的链表上

所使用的时间是不一样的。

 

为了让时间更快,一律使用更快的方式:“元素少的链表”主动接入到“元素多的链表”的尾巴后面。这样,改变的元素个数能更少一些,这个优化被称作“加权合并”。

 

对于M个微信群,平均每个微信群N个用户的场景,用链表的方式表示集合,按照“加权合并”的方式合并集合,最坏的情况下,时间复杂度是O(M*N)。

画外音:假设所有的集合都要合并,共M次,每次都要改变N个元素的根指向,故为O(M*N)。

 

总结

对于“M个群,每个群N个用户,微信群求覆盖”问题,核心思路三步骤:

(1) 全部元素全局排序;

(2) 全局排序后,不同集合中的相同元素,一定是相邻的,通过相同相邻的元素,一次性找到所有需要合并的集合;

(3) 合并这些集合,算法完成;

 

其中:

步骤(1),全局排序,时间复杂度O(M*N);

步骤(2),染色思路,能够迅猛定位哪些集合需要合并,每个元素增加一个属性指向集合根,实现O(1)级别的元素定位集合;

步骤(3),使用链表表示集合,使用加权合并的方式来合并集合,合并的时间复杂度也是O(M*N);

 

总时间复杂度是:

O(M*N)    //排序

+

O(1)        //由元素找到需要合并的集合

*

O(M*N)    //集合合并

 

希望大家有收获。

 

这几篇文章是在讲解决问题的思路,是希望大家从思路中得到启示,如果阅读完文字,能引发一些思考,能有一些收获,就是好的。是不是“并查集”真的这么重要么?

 

思路比结论重要,有收获就是好的。

下一篇介绍“并查集”。

架构师之路-分享可落地的技术文章

相关推荐:

《暴力法求解“微信群覆盖”》

《染色法求解“微信群覆盖”》

这篇关于“微信群覆盖”,线性求解方案?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/437465

相关文章

MySQL分表自动化创建的实现方案

《MySQL分表自动化创建的实现方案》在数据库应用场景中,随着数据量的不断增长,单表存储数据可能会面临性能瓶颈,例如查询、插入、更新等操作的效率会逐渐降低,分表是一种有效的优化策略,它将数据分散存储在... 目录一、项目目的二、实现过程(一)mysql 事件调度器结合存储过程方式1. 开启事件调度器2. 创

numpy求解线性代数相关问题

《numpy求解线性代数相关问题》本文主要介绍了numpy求解线性代数相关问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 在numpy中有numpy.array类型和numpy.mat类型,前者是数组类型,后者是矩阵类型。数组

vue解决子组件样式覆盖问题scoped deep

《vue解决子组件样式覆盖问题scopeddeep》文章主要介绍了在Vue项目中处理全局样式和局部样式的方法,包括使用scoped属性和深度选择器(/deep/)来覆盖子组件的样式,作者建议所有组件... 目录前言scoped分析deep分析使用总结所有组件必须加scoped父组件覆盖子组件使用deep前言

Python绘制土地利用和土地覆盖类型图示例详解

《Python绘制土地利用和土地覆盖类型图示例详解》本文介绍了如何使用Python绘制土地利用和土地覆盖类型图,并提供了详细的代码示例,通过安装所需的库,准备地理数据,使用geopandas和matp... 目录一、所需库的安装二、数据准备三、绘制土地利用和土地覆盖类型图四、代码解释五、其他可视化形式1.

Java解析JSON的六种方案

《Java解析JSON的六种方案》这篇文章介绍了6种JSON解析方案,包括Jackson、Gson、FastJSON、JsonPath、、手动解析,分别阐述了它们的功能特点、代码示例、高级功能、优缺点... 目录前言1. 使用 Jackson:业界标配功能特点代码示例高级功能优缺点2. 使用 Gson:轻量

Redis KEYS查询大批量数据替代方案

《RedisKEYS查询大批量数据替代方案》在使用Redis时,KEYS命令虽然简单直接,但其全表扫描的特性在处理大规模数据时会导致性能问题,甚至可能阻塞Redis服务,本文将介绍SCAN命令、有序... 目录前言KEYS命令问题背景替代方案1.使用 SCAN 命令2. 使用有序集合(Sorted Set)

MyBatis延迟加载的处理方案

《MyBatis延迟加载的处理方案》MyBatis支持延迟加载(LazyLoading),允许在需要数据时才从数据库加载,而不是在查询结果第一次返回时就立即加载所有数据,延迟加载的核心思想是,将关联对... 目录MyBATis如何处理延迟加载?延迟加载的原理1. 开启延迟加载2. 延迟加载的配置2.1 使用

Android WebView的加载超时处理方案

《AndroidWebView的加载超时处理方案》在Android开发中,WebView是一个常用的组件,用于在应用中嵌入网页,然而,当网络状况不佳或页面加载过慢时,用户可能会遇到加载超时的问题,本... 目录引言一、WebView加载超时的原因二、加载超时处理方案1. 使用Handler和Timer进行超

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

高效+灵活,万博智云全球发布AWS无代理跨云容灾方案!

摘要 近日,万博智云推出了基于AWS的无代理跨云容灾解决方案,并与拉丁美洲,中东,亚洲的合作伙伴面向全球开展了联合发布。这一方案以AWS应用环境为基础,将HyperBDR平台的高效、灵活和成本效益优势与无代理功能相结合,为全球企业带来实现了更便捷、经济的数据保护。 一、全球联合发布 9月2日,万博智云CEO Michael Wong在线上平台发布AWS无代理跨云容灾解决方案的阐述视频,介绍了