如何在O(n)时间内判断一个数组是否为排列?

40

输入: 一个包含整数值1到N(某些整数值可以出现多次!)的只读长度为N的数组和一个固定大小(10,100,1000等 - 不取决于 N)的内存区域。

如何在O(n)的时间复杂度下确定该数组是否表示排列?

--到目前为止我所获得的(一个答案证明这是不好的):

  1. 我使用有限的内存区域来存储数组的总和和乘积。
  2. 我将总和与N *(N + 1)/ 2进行比较,将乘积与N!进行比较。

我知道如果条件(2)成立,我可能会有一个排列。 我想知道是否有办法证明条件(2)足以告诉我是否有一个排列。 到目前为止,我还没有弄清楚...


3
不,这纯粹是为了好玩。 - INS
3
产品 N! 所需的存储空间,严格来说取决于 N。而且严格来说,在 O(N) 的时间内无法对 N 个数字进行乘法运算。 - polygenelubricants
1
我相信这将是一个解决方案:http://aperiodic.net/phil/archives/Geekery/find-duplicate-elements.html - INS
3
几乎重复:https://dev59.com/63VC5IYBdhLWcg3wz0l9这篇文章讨论如何检查一个数组中是否包含n个相同的元素,其中n是数组长度的一部分。答案通过比较排序后的数组中相邻元素来实现。如果有n个连续的相同元素,则该数组包含n个相同的元素。 - Eric Bainville
@Iulian:你提供的文章并没有解决这个问题:它假设该数组不包含值N。 - interjay
这里是另一个无法解决的原因:假设你已经处理了 m 个数中的 n 个,并停止了你的算法。现在你可以(虽然需要一些时间)通过处理任何一个由 n-m 个数字组成的流并查看何时出现排列来确定你已经看到了哪 m 个数字。因此从信息的角度来看,你已经存储了所有的数字 m,因此必须使用线性内存。 - Thomas Ahle
16个回答

0

根据您相对于N的可用空间大小,您可以尝试使用哈希和桶。

也就是说,遍历整个列表,对每个元素进行哈希处理,并将其存储在一个桶中。您需要找到一种方法来减少哈希冲突,但这是一个已经解决的问题。

如果一个元素试图进入一个与它相同的项目的桶中,那么它就是一个排列。

这种类型的解决方案将是O(N),因为您只会一次接触每个元素。

然而,这种方法的问题在于M是否大于N。如果M > N,则此解决方案将很好,但如果M < N,则您将无法以100%的准确度解决问题。


考虑到问题具有O(N)时间复杂度和O(1)空间复杂度,定义上存在一个足够大的N使得M < N。 - Ants Aasma
@Ants同意,但也许这个O(1)的空间是以GB为单位的,而N要小得多。如果已知这一点,他可以使用我的解决方案。但同意,这确实需要在开始时了解很多信息。 - samoz
1
大O概念的定义是N足够大,使得复杂度类支配其他所有内容。大O永远是一个理论练习,实际考虑比如可用的几个GB在解决问题的真实实例时很重要。 - Ants Aasma

0
首先,这里有一个信息论的原因,说明为什么这是可能的。我们可以轻松地在O(N)时间和O(1)空间内检查数组中的数字是否在范围内。要指定任何这样的在范围内数字的数组需要N log N位的信息。但是,要指定一个排列需要大约(N log N)- N位的信息(斯特林逼近)。因此,如果我们在测试期间能够获取N位的信息,我们可能就能知道答案。这在N时间内很容易做到(实际上,使用M静态空间,我们可以相当容易地每步获取log M的信息,并且在特殊情况下,我们可以获取log N的信息)。
另一方面,我们只能在静态存储空间中存储类似于M log N位的信息,这显然远小于N,因此它非常依赖于“排列”和“不是排列”之间的决策表面的形状。
我认为这几乎是可能的,但由于问题设置,还不够完美。我认为应该使用循环技巧(如Iulian提到的链接中所示),但此处手头有尾巴的关键假设失败了,因为你可以使用置换来索引数组的最后一个元素。

0

求和与乘积不能保证正确答案,因为这些哈希值可能会发生冲突,即不同的输入可能会产生相同的结果。如果您想要一个完美的哈希值,即一个单一数字结果,它实际上完全描述了数组的数值组成,那么可以采用以下方法。

假设对于范围在[1, N]内的任何数字i,您都可以生成一个唯一的质数P(i)(例如,P(i)是第i个质数)。现在,您需要做的就是计算所有数字的P(i)的乘积。该乘积将完全且明确地描述您的数组的组成,而不考虑其中的值的顺序。您只需要预先计算出“完美”值(对于排列),并将其与给定输入的结果进行比较即可 :)

当然,像这样的算法并不能立即满足发布的要求。但同时它又太过通用:它允许您检测数组中绝对任何数字组合的排列。在您的情况下,您需要检测特定组合1, 2, ..., N的排列。也许这可以用来简化事情... 可能不行。


0

看起来是要使用堆栈机在数组中查找重复项。

当你提取每个数字并且对于被取出的数字有限的了解时,似乎不可能知道堆栈的完整历史记录。


0

这里有证据表明它是不可能完成的:

假设通过某种手段,你已经在除了最后一个单元格之外的所有单元格中检测到了没有重复项。那么问题就简化为检查最后一个单元格是否包含重复项。

如果你目前没有结构化的问题状态表示,那么你只能对每个单元格执行整个先前输入的线性搜索。很容易看出,这会让你得到一个二次时间算法。

现在,假设通过一些聪明的数据结构,你实际上知道了你期望看到的最后一个数字。那么肯定需要至少足够的位数来存储你要寻找的数字——也许是一个内存单元?但是还有倒数第二个数字和倒数第二个子问题:然后你必须类似地表示一组尚未看到的两个可能的数字。这肯定需要比仅编码一个剩余数字更多的存储空间。通过类似的论证进展,状态的大小必须随着问题的规模增长,除非你愿意接受二次时间最坏情况。

这就是时间和空间的权衡。你可以拥有二次时间和恒定空间,或者线性时间和线性空间。你不能同时拥有线性时间和恒定空间。


0
int solution(int A[], int N) {
  int i,j,count=0, d=0, temp=0,max;
  for(i=0;i<N-1;i++) {
    for(j=0;j<N-i-1;j++) {
      if(A[j]>A[j+1]) {
        temp = A[j+1];
        A[j+1] = A[j];
        A[j] = temp;
      }
    }
  }
  max = A[N-1];
  for(i=N-1;i>=0;i--) {
    if(A[i]==max) {
      count++;
    }
    else {
      d++;
    }
    max = max-1;
  }
  if(d!=0) {
    return 0;
  }
  else {
    return 1;
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接