3

我有两组数据。假设一个是一个人,另一个是一个群体。一个人可以在多个组中,而一个组可以有多个人。我的操作基本上是对组和人员的 CRUD。以及一种确保人员列表位于不同组中的方法(这被称为很多)。

现在我正在考虑制作一张二进制 0 和 1 的表格,水平代表所有人,垂直代表所有组。

我可以通过添加每个二进制列表并与二进制列表的“与”操作进行比较,在 O(n) 时间内执行该方法。

例如

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) 运行时。

4

3 回答 3

2

我不知道你的问题的所有细节,但我的直觉是你可能在这里想太多了。您计划在此数据结构中存储多少对象?如果您要在此处存储大量数据,我建议您使用实际数据库而不是数据结构。您在此处描述的操作类型是关系数据库擅长的经典示例。MySQLPostgreSQL是大型关系数据库的例子,它们可以在睡眠中做这种事情。如果您想要更轻量级的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;
}

这种方法在大型数据集上可能没有很好的性能,但是很容易理解。因为它被封装在它自己的方法中,所以如果你需要更好的性能,它也应该很容易更新。如果您确实需要提高性能,我会考虑覆盖 Person 的 equals 方法,这将使关联映射中的查找更快。从那里您还可以查看自定义类型而不是组的 String,也可以使用覆盖的 equals 方法。这将大大加快上面使用的 contains 方法。

我不太关心性能的原因是,就算法而言,您提到的数字并没有那么大。因为这个方法一找到两个匹配的组就会返回,在最坏的情况下,您将调用 ArrayList.contains 的次数等于存在的组数。在最好的情况下,它只需要被调用两次。只有当您非常非常频繁地调用 checkForSharedGroups 时,性能才可能成为问题,在这种情况下,您最好找到一种不那么频繁地调用它的方法,而不是优化方法本身。

于 2013-06-24T19:00:13.907 回答
0

你考虑过HashTable吗?如果您知道将要使用的所有密钥,则可以使用完美哈希函数,它可以让您实现恒定时间。

于 2013-06-24T18:59:56.540 回答
0

如何为人员和组设置两个独立的实体。内部人员有一组组,反之亦然。

class People{

Set<Group> groups;
//API for addGroup, getGroup

}

class Group{

Set<People> people;
//API for addPeople,getPeople

}

检查(人 p1,人 p2):

1)在 p1,p2 上调用 getGroup
2)检查两个集合的大小,
3)迭代较小的集合,并检查该组是否存在于其他集合(组中)

现在,您基本上可以将 People 对象存储在任何数据结构中。如果大小不固定,最好是链表,否则是数组。

于 2013-06-24T19:04:51.080 回答