在O(n)中查找数组中的所有差异

时间:2011-12-10 05:12:57

标签: java c arrays algorithm

问题:给定排序的数组A,找出A中元素的所有可能差异。

我的解决方案:

for (int i=0; i<n-1; ++i) {
  for (int j=i+1; j<n; ++j) {
    System.out.println(Math.abs(ai-aj));
  }
}

当然,它是O(n ^ 2),但我根本不算数。我在网上看了一下,发现了这个:http://www.careercup.com/question?id=9111881。它说你不能做得更好,但在接受采访时我被告知你可以做O(n)。哪个是对的?

7 个答案:

答案 0 :(得分:18)

首先想到的是你没有使用数组排序的事实。让我们假设它处于递增的顺序(减少可以类似地处理)。

我们也可以使用差异望远镜(i&gt; j)的事实:

a_i - a_j = (a_i - a_(i-1)) + (a_(i-1) - a_(i-2)) + ... + (a_(j+1) - a_j)

现在构建一个新序列,称之为s,它具有简单的差异,意思是(a_i - a_(i-1))。这只需要传递一次(O(n)),您也可以跳过重复,这意味着如果a_i则跳过a_i = a_(i+1)

a_i-a_ji>j的所有可能差异均为s_i + s_(i+1) + ... + s_(j+1)。所以,如果你认为它已经找到它们,那么你可以在O(n)时间内完成它。但是,要打印它们,可能需要n(n-1)/2次呼叫,这绝对是O(n^2)

答案 1 :(得分:11)

例如,对于元素 {2 1 ,2 2 ,...,2 n } < / em>存在n⋅(n-1)/ 2 可能的差异,并且它们中没有两个是相等的。所以有 O(n 2 的差异。

由于你必须枚举所有这些,你至少还需要 O(n 2 时间。

答案 2 :(得分:1)

排序或未排序并不重要,如果你必须计算每个差异,那么就没有办法在n ^ 2之内做到这一点,

问题被问错了,或者你只做O(n)然后再打印42次N:D

答案 3 :(得分:1)

如果面试官喜欢理论游戏,也许他正在考虑使用输入和结果表? 任何问题都有输入大小的限制,并且有一个已知的解决方案,可以通过表查找来解决。鉴于您首先创建并存储了该表,该表可能是 large

因此,如果数组大小有限,可以通过表查找解决问题,这可以在恒定时间内完成(给定一些假设)。当然,即使最大阵列大小 2 (假设32位整数),该表也不适合普通计算机的内存或磁盘。对于较大的阵列最大尺寸,您将进入“不适合已知宇宙”的大小。但是,从理论上讲,它可以做到。

(但实际上,我认为Jens Gustedt的评论更有可能。)

答案 4 :(得分:1)

是的,您肯定可以做到这一点。 要找出O(n)的差异,您将需要使用BitSet(C ++)或相应语言的任何类似数据结构。

初始化两个位,例如A和B 您可以执行以下操作: 对于通过数组的每次迭代: 1-在BitSet A中存储连续的差异 2左移B BitSet B中的3个存储的连续差异 四拍A = A或B

例如,我给出了代码- 这里N是数组的大小

for (int i=1;i<N;i++){
    int diff = arr[i]-arr[i-1];
    A[diff]=1;
    B<<=diff;
    B[diff]=1;
    A=A | B;
}

A中的位为1将是差异。

答案 5 :(得分:0)

您可以通过假设数组内容在排序之前是随机整数来获得另一个反例。那么两个差异,Ai-Aj与Ak-Al,甚至是Ai-Aj与Aj-Ak相同的可能性太小,只有O(n)明显的差异Ai-Aj。

鉴于此,面试官的问题是解释允许O(n)解决方案的特殊情况。一种可能性是数组值都是0..n范围内的数字,因为在这种情况下,最大绝对差值仅为n。

我可以在O(n lg n)中执行此操作,但不能在O(n)中执行此操作。通过大小为n + 1的数组表示数组内容,其中元素i设置为1,其中数组中存在值i。然后使用FFT将数组与自身进行卷积 - 存在差异Ai-Aj = k,其中卷积的第k个元素为非零。

答案 6 :(得分:0)

首先需要对数组进行排序

让我们考虑一个排序数组 ar = {1,2,3,4}

所以我们在 O(n^2) 上做了什么

for(int i=0; i<n; i++)
for(int j=i+1; j<n; j++) sum+=abs(ar[i]-ar[j]);

如果我们在这里精心操作,它会看起来像下面

when i = 0 | sum = sum + {(2-1)+(3-1)+(4-1)}                   
when i = 1 | sum = sum + {(3-2)+(4-2)}               
when i = 2 | sum = sum + {(4-3)}

如果我们都写了

sum = ( -1-1-1) + (2+ -2-2) + (3+3 -3) + (4+4+4 ) 

我们可以看到

索引 0 处的数字加 0 次总和并从总和中减去 3 次。
将索引 1 处的数字与总和相加 1 次,然后从总和中减去 2 次。
将索引 2 处的数字与总和相加 2 次,然后从总和中减去 1 次。
将索引 3 处的数字加到总和中 3 次,然后从总和中减去 0 次。

所以我们可以这么说,

the number at index i will be added to the sum for i time 
and will be substracted from the sum for (n-i)-1 time

那么广义表达式为 每个元素将是

sum = sum + (i*a[i]) – ((n-i)-1)*a[i];