寻找类似表格的数据结构

3
我有两组数据,一组是人员,另一组是群组。一个人可以在多个群组中,而一个群组可以有多个人。我的操作基本上是对群组和人员进行CRUD操作,以及一种方法,确保一组人在不同的群组中(这经常被调用)。
现在我考虑创建一个二进制0和1的表格,水平表示所有人,垂直表示所有群组。
通过将每个二进制列表相加并与二进制列表的“and”操作进行比较,我可以在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)运行时间了。


https://github.com/jparams/data-store - AutomatedMike
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 用于 groups,同样具有覆盖的 equals 方法。这将显著加速以上使用的 contains 方法。
我不太关心性能的原因是,就算对于算法来说,你提到的数字并不是很大。因为该方法一旦找到两个匹配的组就会返回,所以最坏的情况下,您将调用 ArrayList.contains 的次数等于存在的组数。在最好的情况下,只需要调用两次。仅当您非常频繁地调用 checkForSharedGroups 时,性能才可能成为问题,在这种情况下,您最好找到一种方法来减少其调用次数,而不是优化方法本身。

是的,除非有其他原因(如教授)要求以特定方式解决此问题,否则OP应该采用更面向对象的方法。使用面向对象的方法将使后续问题更容易解决,例如 - 如果组需要一些额外属性,例如主席,名称,描述等,该怎么办? - aglassman
谢谢您的建议,我估计最多会有约100个组和10000人。不会有太多数据修改。 唯一经常调用的是检查函数,它接受一个人员列表,并在其中没有任何人属于同一组时返回true,否则返回false。我希望以使用非常少的内存并且能够快速执行此功能的方式存储数据。 - user1181031
我应该提到,我会将组和人员的所有信息存储在其他地方(它们实际上是类),我只需要这个关系表来快速计算这个函数。 - user1181031
1
@user1181031 根据您的评论,我已更新了我的答案,我认为现在它应该更接近您想要的内容了。 - TwentyMiles

0

你考虑过使用哈希表吗?如果你知道所有将要使用的键,那么可以使用完美哈希函数,这将使你实现常数时间。


我不确定你的意思。关键是什么?组还是成员? - user1181031
如果我正确理解你的意思,我会将组设置为键,人员作为值。 - Christina Wofford
我认为以这种方式存储不会使检查函数更快。 - user1181031

0

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

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对象存储在任何数据结构中。如果大小不固定,最好使用链表,否则使用数组。


这个可能可行,我只是在想如果有10,000人,100个组,检查函数是否足够快以在不到一秒的时间内运行? - user1181031
我不确定,但是如果你排除预处理时间(填充这些People对象),我认为这应该会很快。原因是,一旦预处理完成,你只需要比较属于这些人的组,而不像你的情况下需要迭代整个数组来首先计算总和。 - ajay.patel
而且,当您有10000个组时会发生什么?您最终会得到一个10000位数的数字?然后对其进行操作? - ajay.patel
我知道这个组不会超过100。我想要的方法是用二进制,其中每个水平数据都存储为32位整数列表。这样,您可以在32位机器上对每32个布尔值执行“加”或“与”操作,并且对于128位,它将是4个CPU操作。整个数据映射大约为~16kb。 - user1181031

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接