题目:求微信群覆盖 微信有很多群,暴力现进行如下抽象: 可以看到,用户u1加入了g1与g2两个群。信群 画外音,暴力注意: 假设微信有M个群(M为亿级别),每个群内平均有N个用户(N为十级别). 现在要进行如下操作: (1) 如果两个微信群中有相同的解微用户,则将两个微信群合并,信群并生成一个新微信群; 例如,暴力上面的法求覆盖g1和g2就会合并成新的群: 画外音:集合g1中包含u1,集合g2中包含u1,解微合并后的信群微信群g3也只包含一个u1。 (2) 不断的暴力进行上述操作,直到剩下所有的法求覆盖微信群都不含相同的用户为止; 将上述操作称:求群的覆盖。 设计算法,解微求群的覆盖,并说明算法时间与空间复杂度。 画外音:58同城2013年校招笔试题。 对于一个复杂的问题,思路肯定是亿华云计算“先解决,再优化”,大部分人不是神,很难一步到位。先用一种比较“笨”的方法解决,再看“笨方法”有什么痛点,优化各个痛点,不断升级方案。 拿到这个问题,很容易想到的思路是: ***步,如何初始化集合? set这种数据结构,大家用得很多,来表示集合: set有两种最常见的实现方式,一种是树型set,一种是哈希型set。 假设有集合: 树型set的实现如下: 其特点是: 哈希型set实现如下: 其特点是: 画外音:求群覆盖,哈希型实现的初始化更快,复杂度是O(M*N)。 第二步,如何判断两个(多个)集合要不要合并? 集合对set(i)和set(j),判断里面有没有重复元素,如果有,就需要合并,判重的伪代码是: ***行(1)遍历***个集合set(i)中的所有元素element; 画外音:这一步的时间复杂度是O(N)。 第二行(2)判断element是否在第二个集合set(j)中; 画外音:如果使用哈希型set,第二行(2)的平均时间复杂度是O(1)。 这一步的时间复杂度至少是O(N)*O(1)=O(N)。 第三步,如何合并集合? 集合对set(i)和set(j)如果需要合并,只要把一个集合中的元素插入到另一个集合中即可: ***行(1)遍历***个集合set(i)中的所有元素element; 画外音:这一步的时间复杂度是香港云服务器O(N)。 第二行(2)把element插入到集合set(j)中; 画外音:如果使用哈希型set,第二行(2)的平均时间复杂度是O(1)。 这一步的时间复杂度至少是O(N)*O(1)=O(N)。 第四步:迭代第二步与第三步,直至结束 对于M个集合,暴力针对所有集合对,进行重复元素判断并合并,用两个for循环可以暴力解决: 递归调用,两个for循环,复杂度是O(M*M)。 综上,如果这么解决群覆盖的问题,时间复杂度至少是: 画外音:实际复杂度要高于这个,随着集合的合并,集合元素会越来越多,判重和合并的成本会越来越高。 基于“先解决,再优化”的思想,很多优化方向的问题,自然而然的从脑中蹦出: (1) 能不能快速通过元素定位集合? 画外音: (2) 能不能快速进行集合合并? (3) 能不能一次合并多个集合? 经典数据结构,分离集合(disjoint set),它有三类操作: 特别适合用来解决这类集合合并与查找的问题,又称为并查集。 如何利用并查集来解决求“微信群覆盖”问题,是后文将要介绍的内容。 画外音:先介绍“并查集”这一种方案,后续再介绍其他方案。 知道并查集的思路和原理,比知道什么是并查集更重要。 算法,其实还是挺有意思的。 【本文为专栏作者“58沈剑”原创稿件,转载请联系原作者】 戳这里,看该作者更多好文