2010-05-04 59 views
2

我有几个Integer对象的ArrayLists,存储在一个HashMap中。查找出现在一组列表中的所有数字

我想获得每个列表中出现的所有数字(整数对象)的列表(ArrayList)。

我的想法到目前为止是:

  1. 迭代通过每个ArrayList和把所有的值到一个HashSet
    • 这将会给我们一个在列表中的所有值“上市”,但只有一次
  2. 迭代通过HashSet的
    2.1随着每次迭代执行ArrayList.contains()
    2.2如果没有任何ArrayLists为操作返回false,则将该数字添加到包含所有最终值的“主列表”中。

如果你能想出更快或更高效的东西,有趣的是我写这篇文章的时候提出了一个相当好的解决方案。但我仍会发布它,以防万一它对其他人有用。

但当然,如果你有更好的方法,请让我知道。

+0

你的第一解决方案将在O(n)时间做,没有额外的存储空间,我非常怀疑你可以打败它。 – Rubys 2010-05-04 13:10:06

+0

感谢您为我的直觉添加一些严谨;) – Ankur 2010-05-04 13:22:29

+1

如果您的两个列表是[1,1,2]和[1,1,3],您会期望输出是[1,1]还是简单地[1]?即,您是否希望保留重复的内容? – Adamski 2010-05-04 16:37:31

回答

0
  1. 从第一List创建Set(例如HashSet)。
  2. 对于每个剩余列表:
    • 呼叫set.retainAll (list)如果两个listset足够小
    • 否则调用set.retainAll (new HashSet <Integer> (list))

我不能说后阈值步骤2中的第二个变体。变得更快,但我猜可能是> 20大小左右。如果你的名单都很小,你不会打扰这个检查。

正如我记得Apache集合具有更有效的整数结构,如果你不仅关心O(*)部分,而且关于该因素。

+0

这是Ankur第一个解决方案的一个可怕的变种,创建了一个新的HashSet因为地图中的每个列表基本上都会导致你浪费一些O(n^2)空间。这是java,GC是不确定的。 GC可以在未知的时间量之后收集未使用的哈希集,这意味着O(n^2)个内存量将坐在那里,分配,但不能使用。换句话说,浪费了。 – Rubys 2010-05-04 13:56:23

+1

@Rubys:我看不到你在哪里得到O(n^2)。如果我不清楚'set'是第一步创建的。即整个循环都是一样的。在步骤2a创建“中间”集是为了加快查找速度(在'retainAll'中),因为在哈希集中它是(预期的)O(1)对列表中的O(n)。 – doublep 2010-05-04 16:54:38

+0

对于我们所知道的,列表和集合永远不够小,并且在每次迭代中您都会创建一个新的HashSet。 hashet本身将在内存中占用O(n)空间。它不是O(n^2),那是我的不好,它是O(nm)空间,其中n是最大的列表,m是原始集合中列表的数量。您会看到,在每次迭代中,您都会创建一个新的哈希集合,这会耗费O(n)空间。既然你必须把这些指针放在某个地方。因此,在所有的m次迭代中,您将使用O(nm)空间。时间将会是美好的。 – Rubys 2010-05-04 17:27:37

2

你必须改变第1步: - 用最短的名单,而不是你的HashSet(如果不是在最短的名单是不是在所有列表...)

然后调用包含其他列表,并尽快删除值作为一个返回false(并且跳过这个值进一步的测试)

在结束时最短列表将包含答案...

一些代码:

public class TestLists { 

    private static List<List<Integer>> listOfLists = new ArrayList<List<Integer>>(); 

    private static List<Integer> filter(List<List<Integer>> listOfLists) { 

     // find the shortest list 
     List<Integer> shortestList = null; 
     for (List<Integer> list : listOfLists) { 
      if (shortestList == null || list.size() < shortestList.size()) { 
       shortestList = list; 
      } 
     } 

     // create result list from the shortest list 
     final List<Integer> result = new LinkedList<Integer>(shortestList); 

     // remove elements not present in all list from the result list 
     for (Integer valueToTest : shortestList) { 
      for (List<Integer> list : listOfLists) { 
       // no need to compare to itself 
       if (shortestList == list) { 
        continue; 
       } 

       // if one list doesn't contain value, remove from result and break loop 
       if (!list.contains(valueToTest)) { 
        result.remove(valueToTest); 
        break; 
       } 
      } 
     } 

     return result; 
    } 


    public static void main(String[] args) { 
     List<Integer> l1 = new ArrayList<Integer>(){{ 
      add(100); 
      add(200); 
     }}; 
     List<Integer> l2 = new ArrayList<Integer>(){{ 
      add(100); 
      add(200); 
      add(300); 
     }}; 
     List<Integer> l3 = new ArrayList<Integer>(){{ 
      add(100); 
      add(200); 
      add(300); 
     }}; 
     List<Integer> l4 = new ArrayList<Integer>(){{ 
      add(100); 
      add(200); 
      add(300); 
     }}; 
     List<Integer> l5 = new ArrayList<Integer>(){{ 
      add(100); 
      add(200); 
      add(300); 
     }}; 
     listOfLists.add(l1); 
     listOfLists.add(l2); 
     listOfLists.add(l3); 
     listOfLists.add(l4); 
     listOfLists.add(l5); 
     System.out.println(filter(listOfLists)); 

    } 

} 
4

我不确定我了解你的目标。但是,如果你想找到列表<整数>对象的集合的交集,那么你就可以做到以下几点:

public static List<Integer> intersection(Collection<List<Integer>> lists){ 
    if (lists.size()==0) 
     return Collections.emptyList(); 

    Iterator<List<Integer>> it = lists.iterator(); 
    HashSet<Integer> resSet = new HashSet<Integer>(it.next()); 
    while (it.hasNext()) 
     resSet.retainAll(new HashSet<Integer>(it.next())); 

    return new ArrayList<Integer>(resSet); 
} 

此代码在项目总数线性时间运行。实际上这是平均线性时间,因为使用了HashSet。

此外,请注意,如果您在循环中使用ArrayList.contains(),它可能会导致二次复杂性,因为此方法在线性时间内运行,不像HashSet.contains()在恒定时间内运行。

+1

可能值得在while循环中对resSet进行空检查。 – Carl 2010-05-04 18:06:50

+0

哦,你不需要为每个it.next()构造一个新的哈希集 - retainAll对集合起作用,并且在它中重复元素.next()不会影响操作。 – Carl 2010-05-04 18:08:40

+0

编辑:我想对于某些retainAll的情况可以节省一些费用,但是在这种情况下,自定义的方法可能是无论如何。 – Carl 2010-05-04 18:19:39

0

使用谷歌收藏Multiset使这(代表明智)cakewalk(虽然我也喜欢Eyal's answer)。这可能不像其他人的时间/记忆方式那样有效,但是很清楚发生了什么。

假设列表包含本身内没有重复:

Multiset<Integer> counter = HashMultiset.create(); 
int totalLists = 0; 
// for each of your ArrayLists 
{ 
counter.addAll(list); 
totalLists++; 
} 

List<Integer> inAll = Lists.newArrayList(); 

for (Integer candidate : counter.elementSet()) 
    if (counter.count(candidate) == totalLists) inAll.add(candidate);` 

如果名单可能包含重复的元素,它们可以通过一组第一传递:

counter.addAll(list) => counter.addAll(Sets.newHashSet(list)) 

最后,这也是理想如果你想以后可能需要一些额外的数据(比如,某些特定的价值是如何接近的)。

另一种方法略微修改的Eyal的(基本上折叠在一起通过一组过滤列表,然后保持所有重叠元素的动作),并且比以上更轻巧:

public List<Integer> intersection(Iterable<List<Integer>> lists) { 

Iterator<List<Integer>> listsIter = lists.iterator(); 
if (!listsIter.hasNext()) return Collections.emptyList(); 
Set<Integer> bag = new HashSet<Integer>(listsIter.next()); 
while (listsIter.hasNext() && !bag.isEmpty()) { 
    Iterator<Integer> itemIter = listsIter.next().iterator(); 
    Set<Integer> holder = new HashSet<Integer>(); //perhaps also pre-size it to the bag size 
    Integer held; 
    while (itemIter.hasNext() && !bag.isEmpty()) 
    if (bag.remove(held = itemIter.next())) 
    holder.add(held); 
    bag = holder; 
} 
return new ArrayList<Integer>(bag); 
} 
相关问题