我有2组数据。 让我们说一个是人,另一个是群。 一个人可以在多个组中,而一个组可以有多个人。 我的操作基本上是群组和人的CRUD。 以及确保人员列表位于不同组中的方法(可以称之为很多)。
现在我正在考虑制作一个二进制0和1的表,水平表示所有人和垂直所有组。
我可以在O(n)时间内通过添加每个二进制列表来执行该方法,并与二进制列表的“和”操作进行比较。
E.g
Group A B C D
ppl1 1 0 0 1
ppl2 0 1 1 0
ppl3 0 0 1 0
ppl4 0 1 0 0
check (ppl1, ppl2) = (1001 + 0110) == (1001 & 0110)
= 1111 == 1111
= true
check (ppl2, ppl3) = (0110 + 0010) == (0110+0010)
= 1000 ==0110
= false
我想知道是否有一个数据结构已经做了类似的事情,所以我不必自己编写并维护O(n)运行时。
答案 0 :(得分:2)
我不知道你问题的所有细节,但我的直觉是你可能会在这里思考问题。您计划在此数据结构中存储多少个对象?如果您要在此处存储大量数据,我建议您使用实际数据库而不是数据结构。您在此处描述的操作类型是关系数据库擅长的事物的经典示例。 MySQL和PostgreSQL是大规模关系数据库的示例,可以在睡眠中执行此类操作。如果你想要一些较轻的SQLite可能会引起人们的兴趣。
如果您没有需要存储在此数据结构中的大量数据,我建议您保持简单,只有当您确定它不够快,无法满足您的需求时才进行优化去做。作为第一个镜头,我建议使用java内置的List界面来存储你的人物,并使用Map来存储组。你可以这样做:
// Use a list to keep track of People
List<Person> myPeople = new ArrayList<Person>();
Person steve = new Person("Steve");
myPeople.add(steve);
myPeople.add(new Person("Bob"));
// Use a Map to track Groups
Map<String, List<Person>> groups = new HashMap<String, List<Person>>();
groups.put("Everybody", myPeople);
groups.put("Developers", Arrays.asList(steve));
// Does a group contain everybody?
groups.get("Everybody").containsAll(myPeople); // returns true
groups.get("Developers").containsAll(myPeople); // returns false
这绝对不是最快的选择,但是如果你没有大量的人来跟踪,你可能甚至都不会注意到任何性能问题。如果您确实有一些特殊条件会使常规列表和地图的使用速度变得不可行,请发布它们,我们可以根据这些条件提出建议。
修改强>
阅读完您的评论后,我似乎在第一次浏览时误读了您的问题。看起来你对将人群映射到人群并不是那么感兴趣,而是将人们映射到群组。你可能想要的是更像这样的东西:
Map<Person, List<String>> associations = new HashMap<Person, List<String>>();
Person steve = new Person("Steve");
Person ed = new Person("Ed");
associations.put(steve, Arrays.asList("Everybody", "Developers"));
associations.put(ed, Arrays.asList("Everybody"));
// This is the tricky part
boolean sharesGroups = checkForSharedGroups(associations, Arrays.asList(steve, ed));
那么如何实现checkForSharedGroups方法呢?在你的情况下,因为围绕这个的数字非常低,我只是尝试一下天真的方法并从那里开始。
public boolean checkForSharedGroups(
Map<Person, List<String>> associations,
List<Person> peopleToCheck){
List<String> groupsThatHaveMembers = new ArrayList<String>();
for(Person p : peopleToCheck){
List<String> groups = associations.get(p);
for(String s : groups){
if(groupsThatHaveMembers.contains(s)){
// We've already seen this group, so we can return
return false;
} else {
groupsThatHaveMembers.add(s);
}
}
}
// If we've made it to this point, nobody shares any groups.
return true;
}
此方法可能在大型数据集上没有很好的性能,但它很容易理解。因为它封装在它自己的方法中,所以如果事实证明你需要更好的性能,它也应该很容易更新。如果您确实需要提高性能,我会查看overriding the equals method of Person,这将使关联映射中的查找更快。从那里你也可以查看自定义类型而不是组的字符串,也可以使用重写的equals方法。这将大大加快上面使用的包含方法。
我不太关心性能的原因是,就算法而言,你提到的数字并不是那么大。因为此方法在找到两个匹配组后立即返回,在最坏的情况下,您将调用ArrayList.contains多次等于存在的组的数量。在最好的情况下,它只需要被调用两次。如果你非常经常地调用checkForSharedGroups,那么性能可能只是一个问题,在这种情况下,你可能最好找到一种不经常调用它的方法,而不是优化方法本身。
答案 1 :(得分:0)
您考虑过HashTable吗?如果您知道将要使用的所有按键,则可以使用Perfect Hash Function来实现恒定时间。
答案 2 :(得分:0)
如何为People和Group提供两个独立的实体。 Inside People有一组Group,反之亦然。
class People{
Set<Group> groups;
//API for addGroup, getGroup
}
class Group{
Set<People> people;
//API for addPeople,getPeople
}
检查(People p1,People p2):
1)在p1,p2上调用getGroup
2)检查两组的大小,
3)迭代较小的集合,并检查该组是否存在于其他集合(组)中
现在,您基本上可以将People对象存储在任何数据结构中。如果大小不固定,则最好是链表,否则是数组。