编写此程序的最佳方式

时间:2012-03-07 00:34:42

标签: java big-o

我有一个一般的编程问题,我碰巧使用Java来回答。这是个问题:

给定一组int,编写一个程序,以找出数组中有多少不唯一的数字。 (例如,在{2,3,2,5,6,1,3}中,2个数字(2和3)不是唯一的。你的程序执行了多少次操作(用O表示法)?

这是我的解决方案。

int counter = 0;


for(int i=0;i<theArray.length-1;i++){
for(int j=i+1;j<theArray.length;j++){
    if(theArray[i]==theArray[j]){
        counter++;
                    break; //go to next i since we know it isn't unique we dont need to keep comparing it.
            }
}
}

return counter:

现在,在我的代码中,每个元素都与其他元素进行比较,因此有大约n(n-1)/ 2个操作。给O(n ^ 2)。如果您认为我的代码不正确/效率不高或我的O表达错误,请告诉我。

6 个答案:

答案 0 :(得分:1)

您的代码无法执行您想要的操作。如果你使用数组{2, 2, 2, 2}运行它,你会发现它返回2而不是1.你必须找到一种方法来确保计数永远不会重复。

但是,您的Big O表达式作为最坏情况分析是正确的,因为可能与每个其他元素进行比较。

答案 1 :(得分:1)

为什么不使用Map,如以下示例所示:

// NOTE! I assume that elements of theArray are Integers, not primitives like ints
// You'll nee to cast things to Integers if they are ints to put them in a Map as
// Maps can't take primitives as keys or values
Map<Integer, Integer> elementCount = new HashMap<Integer, Integer>();
for (int i = 0; i < theArray.length; i++) {
   if (elementCount.containsKey(theArray[i]) {
     elementCount.put(theArray[i], new Integer(elementCount.get(theArray[i]) + 1));
   } else {
     elementCount.put(theArray[i], new Integer(1));
   }
}

List<Integer> moreThanOne = new ArrayList<Integer>();
for (Integer key : elementCount.keySet()) { // method may be getKeySet(), can't remember
   if (elementCount.get(key) > 1) {
      moreThanOne.add(key);
   }
}

// do whatever you want with the moreThanOne list

请注意,此方法需要遍历列表两次(我确信有一种方法可以迭代一次)。它通过theArray迭代一次,然后在迭代elementCount的密钥集时再次隐式,如果没有两个元素相同,则将完全一样。但是,连续两次迭代相同的列表仍然是O(n)而不是O(n ^ 2),因此具有更好的渐近运行时间。

答案 2 :(得分:1)

您的分析是正确的,但您可以很容易地将其分解到O(n)时间。在遍历数组时,尝试使用HashMap<Integer,Integer>存储以前看到的值(键是您看过的数字,值是您看过它的次数)。每次尝试将一个整数添加到hashmap中时,请检查它是否已经存在。如果是,只需增加整数计数器。然后,在结束时,循环遍历地图并计算您看到具有大于1的相应值的键的次数。

答案 3 :(得分:0)

我认为O(n^2)的时间复杂度是正确的。

如果空间复杂性不是问题,那么您可以使用256个字符(ASCII)标准的数组并开始用值填充它。例如 //也许您可能需要将所有值初始化为0.我不知道。但是O(n+m)可以完成以下操作:其中n是阵列的长度,m是阵列的长度。

  

int [] array = new int [256];

     

for(int i = 0; i&lt; theArray.length(); i ++)

  array[theArray[i]] = array[theArray[i]] + 1;
     

for(int i = 0; i&lt; array.length(); i ++)

  if(array[i] > 1)

        System.out.print(i);

答案 4 :(得分:0)

首先,你的方法就是我所谓的“暴力”,在最坏的情况下确实是O(n ^ 2)。它也被错误地实现,因为重复n次的数字被计为n-1次。

除此之外,还有很多方法可以解决这个问题。第一个(许多答案已经建议)是迭代数组,并使用地图来跟踪给定元素被看到的次数。假设地图使用哈希表作为底层存储,平均情况复杂度应为O(n),因为从地图获取和插入的平均值应为O(1),并且您只需要迭代列表和地图每一次。请注意,在最坏的情况下,这仍然是O(n ^ 2),因为无法保证散列会产生持续时间结果。

另一种方法是首先简单地对数组进行排序,然后迭代排序的数组以查找重复数据。这种方法完全取决于所选择的排序方法,可以是从O(n ^ 2)(对于天真的冒泡排序)到O(n log n)最坏情况(对于合并排序)到O(n log n)的任何地方。 )平均 - 尽管 - 可能的情况(快速排序)。

使用排序方法,假设数组中有任意对象,这是最好的。但是,由于你的例子涉及整数,你可以通过使用基数排序来做得更好,基数排序将具有O(dn)的最坏情况复杂度,其中d基本上是常数(因为对于32位整数,它最大值为9)。

最后,如果您知道元素是整数,并且它们的幅度不是太大,您可以使用大小为ElementMax的数组来改进基于映射的解决方案,这将保证O(n)最坏情况复杂性,需要4 * ElementMax额外字节的内存。

答案 5 :(得分:0)

正如其他人所说,使用哈希很可能有O(n)解决方案。在Perl:

my @data = (2,3,2,5,6,1,3);
my %count;
$count{$_}++ for @data;
my $n = grep $_ > 1, values %count;
print "$n numbers are not unique\n";

<强>输出

2 numbers are not unique