2010-05-04 60 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); 
} 
相關問題