问题陈述
人们在社交网络中相互联系。人I和人J之间的联系表示为M I J.当属于不同社区的两个人联系时,净效应是我和J所属的两个社区的合并。
一开始,有N个人代表N个社区。假设人1和2连接,后来2和3连接,则1,2和3将属于同一社区。 p>
有两种类型的查询:
M I J =>包含人I和J的社区合并(如果他们属于不同的社区)。
Q I =>打印我所属的社区的大小。
我的方法:
我创建了一组空集。当两个人合并时,我正在检查所有内部集合,如果找到任何内部集合,我将它们添加到该集合并突破。 如果不是,我正在与这些人创造一个新的内部集合。 现在在这个父集合中,我需要将所有内部集合相互比较,如果找到了交集,我应该组合两个内部集合,这是我无法做到的。
我的方法是否正确?但这是一个非常迭代的过程,有没有更好的方法来解决它?
我的代码:
import java.io.*;
import java.util.*;
import java.text.*;
import java.math.*;
import java.util.regex.*;
public class Solution {
public static void main(String[] args) {
/* Enter your code here. Read input from STDIN. Print output to STDOUT. Your class should be named Solution. */
Scanner sc = new Scanner(System.in);
int nPeople = sc.nextInt();
int queries = sc.nextInt();
Set<Set<Integer>> community = new HashSet<Set<Integer>>();
for(int i = 0 ; i<queries ; i++){
char query = sc.next().charAt(0);
if(query == 'Q'){
int p = sc.nextInt();
Set<Integer> tmpset = new HashSet<Integer>();
for( Set<Integer> innerSet : community){
for(Integer person : innerSet) {
if( person == p ){
for(Integer each : innerSet){
tmpset.add(each);
}
}
}
}
if(tmpset.size()!= 0) {
System.out.println(tmpset.size());
}
else {
System.out.println("1");
}
}
else if(query=='M'){
int person1 = sc.nextInt();
int person2 = sc.nextInt();
int c = 0;
loop:
for( Set<Integer> innerSet : community){
for(Integer person : innerSet) {
if( person == person1 || person == person2){
innerSet.add(person1);
innerSet.add(person2);
c++;
break loop;
}
}
}
if(c==0){
Set<Integer> tmpset = new HashSet<Integer>();
tmpset.add(person1);
tmpset.add(person2);
community.add(tmpset);
}
}
}
}
}
我的代码输出:设置包含集。
问题链接:https://www.hackerrank.com/challenges/merging-communities
在@Adamski的帮助下解决了它,使用了不相交集数据结构,但仍然没有那么有效的解决方案。
代码:
import java.io.*;
import java.util.*;
import java.text.*;
import java.math.*;
import java.util.regex.*;
public class Solution {
public static void main(String[] args) {
/* Enter your code here. Read input from STDIN. Print output to STDOUT. Your class should be named Solution. */
Scanner sc = new Scanner(System.in);
int nPeople = sc.nextInt();
DisJoint comm = new DisJoint(nPeople);
int queries = sc.nextInt();
for(int k = 0 ; k<queries ; k++){
char query = sc.next().charAt(0);
if(query == 'Q'){
int person = sc.nextInt();
int personParent = comm.Find(person);
int community = 0;
for(int j =1 ; j<nPeople+1 ; j++){
int tmpParent = comm.Find(j);
if(personParent == tmpParent){
community++;
}
}
System.out.println(community);
}
if(query == 'M'){
int person1 = sc.nextInt();
int person2 = sc.nextInt();
comm.Union(person1,person2);
}
}
}
}
class DisJoint{
public int Count;
public int[] Parent;
public int[] Rank;
public DisJoint(int count){
this.Count = count;
this.Parent = new int[this.Count+1];
this.Rank = new int[this.Count+1];
for (int i = 1; i < this.Count+1; i++) {
this.Parent[i] = i;
this.Rank[i] = 0;
}
}
public int Find(int i){
if(i == Parent[i]){
return Parent[i];
}
else{
int result = Find(Parent[i]);
Parent[i] = result;
return result;
}
}
public void Union(int a, int b){
if(a>b){
int tmp = a;
a = b;
b = tmp;
}
int aroot = this.Find(a);
int broot = this.Find(b);
int arank = Rank[aroot];
int brank = Rank[broot];
if (aroot == broot){
return;
}
if (arank < brank) {
this.Parent[aroot] = broot;
}
else if (arank > brank) {
this.Parent[broot] = aroot;
}
else{
this.Parent[aroot] = broot;
Rank[broot]++;
}
}
}
请测试上述链接中的代码。
答案 0 :(得分:2)
您是否考虑过使用数据结构来表示不相交的集合?例如:http://www.mathblog.dk/disjoint-set-data-structure/
基本前提是您定义一个类(例如Person
)并将您的社区集表示为单个数组。每个Person
都包含一个返回数组的索引,指向自身或指向另一个Person
:
| Adam | Dave | Fred | Tom | James |
| 0 | 0 | 1 | 3 | 3 |
在上面的例子中,为了测试Fred和Dave是否在同一个社区中,你从每个人开始并按照图表到根人;即索引引用自己的人:
Fred -> Dave -> Adam
Dave -> Adam
(显然这里有一个优化,在第一次遍历期间,你在到达根之前实际遇到Dave。)
相比之下,测试詹姆斯和弗雷德是否在同一社区:
James -> Tom
Fred -> Dave -> Adam
人们有不同的根源,因此属于不同的社区。 p>
合并社区就是将一个社区的根人重新分配给另一个社区的根。
推断社区规模更为复杂;我会把这作为练习让你弄清楚!
答案 1 :(得分:0)
首先,关于你的代码。这是糟糕的OO设计:
现在,关于解决方案,我采取了另一种方法:
所以我创建了以下代码(测试和工作) 注意:因为在问题中,索引从1开始,我用size == nPeople定义了“world”,并且没有使用它的0索引。
public class Solution
{
// data structure: index is person and value is community
static List<Integer> world;
// input
static int nPeople, queries;
static Scanner sc = new Scanner(System.in);
public static void main(String[] args)
{
/* Enter your code here. Read input from STDIN. Print output to STDOUT. Your class should be named Solution. */
System.out.println("people operations:");
init();
for (int i = 0; i < queries; i++) {
System.out.println("next operation:");
char operation = sc.next().charAt(0);
switch (operation) {
case 'Q':
System.out.println(query(sc.nextInt()));
break;
case 'M':
merge(sc.nextInt(), sc.nextInt());
System.out.println("done");
break;
}
}
}
// init world
private static void init()
{
nPeople = sc.nextInt();
queries = sc.nextInt();
world = new ArrayList<>();
for (int i = 0 ; i <= nPeople ; i++) world.add(i);
}
// get the community of specified person
// return size
private static int query(Integer person)
{
return getCommunity(person).size();
}
// check that the two specified persons do not belong to same community
// get the two communities of specified persons
// iterate over list of persons of comunity2,
// set them to community (value) of person1
private static void merge(Integer person1, Integer person2)
{
if (world.get(person1).equals(world.get(person2))) return;
List<Integer> community1 = getCommunity(person1);
List<Integer> community2 = getCommunity(person2);
if (community1.isEmpty() || community2.isEmpty()) return;
Integer community1Id = world.get(community1.get(0));
for (Integer person : community2) {
world.set(person, community1Id);
}
}
// returns list of persons (indexes in world)
// that belong to community (value) of specified person
private static List<Integer> getCommunity(Integer person)
{
List<Integer> community = new ArrayList<>();
for (int i = 0 ; i < world.size() ; i++) {
if (world.get(i).equals(world.get(person))) {
community.add(i);
}
}
return community;
}
}
答案 2 :(得分:0)
我已经找到了问题的完整解决方案。谢谢大家。没有你的帮助,我无法做到这一点。所有测试用例都被接受了。
import java.io.*;
import java.util.*;
import java.text.*;
import java.math.*;
import java.util.regex.*;
public class Solution {
static int parent[],rank[],m[];
static int find(int i)
{
if(i==parent[i])
return i;
int res=find(parent[i]);
parent[i]=res;
return res;
}
static void union(int i,int j)
{
int irep=find(i),jrep=find(j);
if(irep==jrep)
return;
if(rank[irep]>rank[jrep])
{
parent[jrep]=irep;
m[irep]+=m[jrep];
}
else if(rank[irep]<rank[jrep])
{
parent[irep]=jrep;
m[jrep]+=m[irep];
}
else
{
parent[jrep]=irep;
rank[irep]++;
m[irep]+=m[jrep];
}
}
public static void main(String []args)throws IOException
{
Scanner sc=new Scanner(System.in);
int n=sc.nextInt(),q=sc.nextInt();
parent=new int[n+1];
rank=new int[n+1];
m=new int[n+1];
for(int i=0;i<=n;i++)
{
parent[i]=i;
rank[i]=1;
m[i]=1;
}
for(int i=0;i<q;i++)
{
switch(sc.next().charAt(0))
{
case 'Q':
int c=0;
System.out.println(m[find(sc.nextInt())]);
break;
case 'M':
union(sc.nextInt(),sc.nextInt());
break;
}
}
}
}
答案 3 :(得分:0)
让P = {P1,...,Pn}
成为一群人。由于尚未连接,因此每个社区的大小为1
。
P1 -> Community size = 1
P2 -> Community size = 1
P3 -> Community size = 1
...
Pn -> Community size = 1
如果我们合并P1
和P2
,那么P2
可以跟随P1
,因此它们都成为同一个社区的一部分。为了清楚起见-P2
跟在P1
之后,因为P1
的社区规模并不小。
P2 -> P1 -> Community size = 2
P3 -> Community size = 1
...
Pn -> Community size = 1
因此,现在,如果我们检查P2
属于哪个社区,我们会发现一个跟随P2
的人-它是P1
。但是,由于P1
没有跟随其他任何人,因此P2
属于P1
(每个社区都由其关注最多的成员定义)。
如果我们合并P2
和P3
,则随着P3
社区规模变小,他必须遵循与P2
相同的跟随者。由于P1
是P2
的关注最多的人,因此P3
开始关注P1
,因此成为P1
的一部分。
P2 -> P1 -> Community size = 3
^
|
P3
...
Pn -> Community size = 1
我们可以进一步继续该过程,但是现在很明显,此配置可以表示为一个数组(根据问题描述,人数限制为100,000),其中索引对应于一个人,其值对应于其关注的最高社区会员。
另一个数组可以代表每个人的社区大小(索引->人,值->大小)。但是,我们将仅针对关注人数最多的社区而不是社区的每个成员进行更新,因为我们始终可以根据任何成员联系高层社区,从而找到社区规模。
然后,在任何2
个人Pi
和Pj
的每次合并过程中,我们发现其跟随的最高代表-topPi
和topPj
。如果它们相同-没有理由进行合并,因为Pi
和Pj
已经属于同一社区。如果不是,请加入更大的社区,并相应地更新其大小。
要显示任何人的社区规模,请找到其最高的关注者代表(递归转到当前关注者的下一个人),并获取该代表者社区的规模。
现在输入一些代码。
最初,每个人都在一个单独的社区中。人和社区由相同的正整数值标识。每个社区的大小为1
。
int people[100001];
int sizes[100001];
void init(int n) {
for (int i = 1; i <= n; i++) {
people[i] = i;
sizes[i] = 1;
}
}
要找到给定人员关注度最高的社区,我们需要一个功能:
int findTopFollowedPerson(int x) {
if (people[x] != x) {
return findTopFollowedPerson(communities[x]);
}
return x;
}
要合并2
和x
的任何y
个人,请提供一种merge
方法:
void mergeCommunities(int x, int y) {
x = findTopFollowedPerson(x);
y = findTopFollowedPerson(y);
if (x == y) {
return;
}
if (sizes[x] >= sizes[y]) {
people[y] = x;
sizes[x] += sizes[y];
} else {
people[x] = y;
sizes[y] += sizes[x];
}
}
然后,要检查任何人的社区的大小,请返回跟随人数最多的人的社区的大小:
int communitySize(int x) {
int root = findTopFollowedPerson(x);
return sizes[root];
}