2012-03-05 79 views
1

我有一個數組,大小可以達到10000.它只包含1/2/3/4。我需要找出陣列中有多少個1s,2s,3s和4s。最快的做法是什麼?我的使用語言是Java。我的一段代碼 -快速查找數組中元素的數量

for(int i=0; i<myArray.length;i++){ 
      int element = myArray[i]; 
      if(element == 1){ 
       onesCount++; 
      } 
      else if(element == 2){ 
       twosCount++; 
      } 
      else if(element == 3){ 
       threesCount++; 
      } 
      else 
       foursCount++; 
} 

我希望有一個很好的解決方案。

+0

你想要一個快速的方法,還是最快的方法? :) – 2012-03-05 07:18:40

+1

因爲無論如何你要解析整個數組,所以無論你如何做,你的運行時間必須是'O(n)'。 – noMAD 2012-03-05 07:19:05

+0

最快的方法。 – sgowd 2012-03-05 07:19:09

回答

7
int count[5]; //initialize this to 0 
for(int i = 0; i<n; i++) 
{ 
count[array[i]]+=1; 
} 
+1

雖然它仍然是O(n),但這個可以節省一些比較。 – 2012-03-05 07:59:13

+0

OTOH,它現在必須更新堆上的數組而不是堆棧上的本地int。 – Thilo 2012-03-05 08:00:23

+0

這可能比我的更好。我認爲這樣做不安全會贏。 – 2012-03-05 08:01:59

0

您可以在數組中使用一次。

只要有自己的四個要素,分別代表你的價值觀之一(1/2/3/4)的數組和原始數組中的每個元素,你的「伯爵」陣列中增加了計數,在相應的地方。 這將使它O(n)。

0

如果您可以改爲使用地圖或自定義類。如果你不能遍歷整個數組。如果你現在不用擔心性能問題,那麼我只需要進行迭代。

2

您將擁有用於數組條目的單獨計數器。每個將在一個新的匹配數量遞增被發現,所以你必須訪問每一個指數至少一次,也就是說,你將有一個算法O(n)的時間工作。開關語句可能是首選,而不是多個if-else語句:

int[] array = new int[10000]; 
// ... populate array 
int[] counters = new int[4]; 

for (int i = 0; i < array.length; i++) 
{ 
    int temp = array[i]; 
    switch (temp) { 
    case 1: 
     counters[0]++; 
     break; 
    case 2: 
     counters[1]++; 
     break; 
    case 3: 
     counters[2]++; 
     break; 
    case 4: 
     counters[3]++; 
     break; 
    default: 
     // to do. 
    } 
} 
+0

哎呀!我忘記了開關盒。它可能會消除大量的比較。但爲什麼數組計數器而不是計數變量(就像我的問題)。任何原因? – sgowd 2012-03-05 09:48:17

+0

沒有特定的原因,但它更靈活。如果你想打印所有的計數器,你只需要用for循環迭代計數器數組。不是很好嗎? – Juvanis 2012-03-05 09:53:39

2

沒有解決方案比您的解決方案更好。可以更靈活,但不會更快。一切都必須至少通過整個陣列。

的性能優化的唯一區域是通過跟蹤計數器作爲陣列被更新,以避免在執行此操作,例如。如果這是值得的麻煩(可能不是)取決於你需要多久做一次這樣的事情,這個數組有多大,以及你需要做什麼。

1

如果您使用的是Java 7,則可以使用Fork/Join Framework。 的複雜性仍然是O(N)...但它可能更快的大陣

+0

+1。完全不知道在這種情況下這是否合理,但總的來說,這對於並行處理來說是一項完美的任務。 – Thilo 2012-03-05 08:01:25

+1

@Thilo,是的,線程創建的開銷可能對此操作太大,但我認爲值得嘗試 – hage 2012-03-05 08:06:22

0

如果開關的版本(deporter)或遊牧的版本不是最好的,試試這個:

for (int i = 0; i < myArray.length; i++) { 
    int element = myArray[i]; 
    if (element > 2) { 
     if (element == 4) { 
      foursCount++; 
     } else 
      threesCount++; 
    } else { 
     if (element == 2) 
      twosCount++; 
     else 
      onesCount++; 
    } 
} 

它可以節省少量的比較。但這取決於真實的數據。如果你對數據感到幸運,那麼簡單的版本可能會做得更好。

除此之外,採用並行始終是值得大數據一試。

0

我不認爲你會得到比這更快:

final int[] counts = new int[5]; 
final int length = array.length - 1; 
for (int i = 0; i < length; i++) { 
    counts[array[i]]++; 
} 

注意的是,在循環,array.length未被引用。它被放到一個本地final int中,這樣可以避免在每次迭代中對array.length進行解引用。

我爲基準此與使用一個開關此方法..case語句,只有局部堆棧變量:

int count1 = 0; 
    int count2 = 0; 
    int count3 = 0; 
    int count4 = 0; 

    for (int i = array.length - 1; i >= 0; i--) { 
     switch (array[i]) { 
     case 1: 
      count1++; 
      break; 
     case 2: 
      count2++; 
      break; 
     case 3: 
      count3++; 
      break; 
     case 4: 
      count4++; 
      break; 
     } 
    } 

結果是第一種方法花了17300納秒,而switch..case方法了79800個納秒。 [更新:忘了劃分納秒十。我運行每種方法10次。]

注意:我做基準測試之前先提醒虛擬機。

+0

您是如何測量的?發佈版本? JIT優化?調試器連接?數據大小?重複?緩存感知?你可以發佈一些代碼嗎?充其量:兩個版本的組裝? – 2012-03-05 08:08:14

+0

我用'-server'運行虛擬機,並通過對這兩種方法(服務器JIT熱點編譯閾值爲10000次迭代)的11000次調用進行加熱。然後在調用這兩個方法的每一個之前和之後使用System.nanoTime()10次。是的,用調試編譯。不,沒有附加調試器。數據大小爲10000個整數。 – brettw 2012-03-05 08:15:39

+0

運行javap轉儲這兩種方法的字節碼是給讀者留下的練習。只要說第一種方法是字節碼的32'行',第二種方法是105.第二種方法使用'tableswitch'字節碼以及很多'goto'跳轉,最後效率低得多由基準)。 – brettw 2012-03-05 08:26:07