2012-03-05 51 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