【问题标题】:(Algorithm) Find if two unsorted arrays have any common elements in O(n) time without sorting?(算法)查找两个未排序的数组是否在 O(n) 时间内有任何公共元素而不进行排序?
【发布时间】:2010-12-28 16:17:55
【问题描述】:

我们有两个未排序的数组,每个数组的长度为 n。这些数组包含 0-n100 范围内的随机整数。如何查找这两个数组在 O(n)/线性时间内是否有任何共同元素?不允许排序。

【问题讨论】:

  • 闻起来像家庭作业。如果是,请标记为这样。
  • 所以答案是肯定的/否定的,如果它们有 any 个共同的元素是肯定的,否则没有?
  • 存储怎么样? O(n)?
  • 存储不重要。你是对的,这是一个是/否的问题。
  • IMO,meriton 目前接受的答案是无用的。 (向meriton道歉)。

标签: arrays algorithm


【解决方案1】:

哈希表会拯救你。真的,它就像一把用于算法的瑞士刀。
只需将第一个数组中的所有值都放入其中,然后检查第二个数组中是否存在任何值。

【讨论】:

  • 使用哈希表是 O(nlog n)。
  • @Neil:搜索通常是 O(1),构建是 O(n)。
  • 虽然这在实践中是一个很好的方法,但它并没有达到 O(n) worst case 性能。首先,因为哈希表查找在最坏的情况下是 O(n)(错误的哈希函数,并且对于每个哈希函数,都有一个包含许多哈希冲突的输入),其次是因为您隐含地假设哈希函数可以在恒定时间内计算,即使用于保存数字的位数不是恒定的。
  • @Neil 它是 O(1),具有提供的条件。有一个简单的证明:en.wikipedia.org/wiki/Hashtable#Performance_analysis 更不用说,哈希表通常与对数无关。
  • @Nikita:每个条目最多 n^100,我认为可以存储 100*log(n) 存储。所以我猜存储是O(n log n)。这是我对此的第三个答案,所以没有人应该相信它。
【解决方案2】:

您尚未定义计算模型。假设您只能在 O(1) 时间内读取 O(1) 位(其他任何东西都将是一个相当奇特的计算模型),那么在 O(n) 最坏情况下的时间复杂度中没有算法可以解决问题。

校样草图: 输入中的每个数字都占用 O(log(n ^ 100)) = O(100 log n) = O(log n) 位。因此,整个输入为 O(n log n) 位,在 O(n) 时间内无法读取。因此,任何 O(n) 算法都无法读取整个输入,因此如果这些位重要,则不会做出反应。

【讨论】:

  • 这个问题可以使用基数排序或类似的排序算法在 O(n) 时间内解决。但是这里的问题是不允许排序。
  • @Omerta:除非您使用一些不寻常的计算模型,否则即使使用基数排序也无法在 O(n) 最坏情况下解决。
  • 基数排序是 O(kn),其中 n 是数字的计数,k 是数字的长度。如果 k 是常数,即 O(n),但这里 k 是 O(log n) ...
  • -1:这违背了问题的精神。此外,Word RAM 模型并不是那么奇特。事实上,大多数算法时间复杂度分析在未指定时都会隐式使用:O(1) 时间数组访问、O(1) 两个适合寄存器的整数相加等。
  • “寄存器”和“字”都有固定的大小,与 n 无关。在这里,随着 n 的增加,数字会变长,因此无法将它们存储在恒定数量的“单词”或“寄存器”中。
【解决方案3】:

回答尼尔: 由于您一开始就知道 N 是多少(两个大小为 N 的数组),您可以创建一个数组大小为 2*N*some_ratio 的散列(例如:some_ratio= 1.5)。有了这个大小,几乎所有简单的散列函数都会为您提供良好的实体分布。

您还可以实现 find_or_insert 来搜索现有的或在同一操作中插入新的,这将减少哈希函数和比较调用。 (c++ stl find_or_insert 不够好,因为它不能告诉您该项目之前是否存在)。

【讨论】:

  • 他说存储不是问题。哈希表是存储和访问速度之间的折衷。如果没有必要,我为什么要牺牲存储空间?
  • 如果值范围可以适合 RAM 内存 - 那么是的,您的方法很好,但 32 位范围是 4G 范围,即使在今天也被认为非常大。 (4GB Ram 是不够的,因为您需要额外的内存,所以不是每台计算机都可以在 RAM 上运行它)。如果您采用 64 位范围 - 毫无疑问,您将永远不会有足够的内存。
【解决方案4】:

线性检验

使用 Mathematica 哈希函数和任意长度的整数。

测试到 n=2^20,生成随机数直到 (2^20)^100 = (大约 10^602)

以防万一……程序是:

k = {};
For[t = 1, t < 21, t++,
  i = 2^t;
  Clear[a, b];
  Table[a[RandomInteger[i^100]] = 1, {i}];
  b = Table[RandomInteger[i^100], {i}];
  Contains = False;
  AppendTo[k,
   {i, First@Timing@For[j = 2, j <= i, j++,
       Contains = Contains || (NumericQ[a[b[[j]]]]);
       ]}]];
ListLinePlot[k, PlotRange -> All, AxesLabel -> {"n", "Time(secs)"}]

【讨论】:

  • 呵呵,男人拿着图表出现了,房间里安静了下来。好吧,我们知道哈希表具有线性平均案例性能。
  • @GregS 这种情况适用于任意长度的整数。我们都知道哈希部分。
  • 评估 a[b[j]] 需要 O(log n) 个步骤,因为 b[j] 包含 O(log n) 个数字。平均而言,b 中的第一个重复出现在位置 n/2。事实上,您的示例可能不会在列表之间产生重复,因此您必须在失败之前遍历整个列表。无论图表显示什么,对我来说听起来都像 O(n log n)。
  • @GregS 这就是我首先制作图形的原因。我的赌注不是线性的。也许我们可以进行另一项测试来展示您的洞察力?
【解决方案5】:

将第一个数组的元素放入哈希表中,并检查是否存在扫描第二个数组。这为您提供了 O(N) 平均情况下的解决方案。

如果您想要一个真正的 O(N) 最坏情况解决方案,那么不要使用哈希表,而是使用 0-n^100 范围内的线性数组。请注意,每个条目只需使用一个位。

【讨论】:

  • 没有错,只是初始化数组需要O(n log n)。
  • 如何在 O(n) 时间内初始化该大小为 O(n^100) 的数组?您的算法假定数组中的所有位最初都是 0。
  • @GregS:在我们的理论模型中,内存访问应该是 O(1),对于堆上的分配也是如此:O(1) 摊销时间。所以用全零初始化数组应该只需要 O(N) 时间。
  • 抱歉这里有误会。当我说 O(N) 时,N 不是 n^100 的东西,而是 N 是 0-n^100 范围内的元素数。对不起,不准确。现在我看到这个原始海报问题看起来有点陌生......
  • @antirez:什么理论内存模型?没有提供。
【解决方案6】:

如果存储不重要,则从头开始哈希表以支持长度为 n 的数组。当您在第一个数组中遇到该数字时,将其标记为 true。在通过第二个数组时,如果您发现其中任何一个是正确的,那么您就有了答案。 O(n)。

Define largeArray(n)
// First pass
for(element i in firstArray)
   largeArray[i] = true;

// Second pass
Define hasFound = false;
for(element i in secondArray)
   if(largeArray[i] == true) 
      hasFound = true;
      break;

【讨论】:

  • 嗯?定义“当您在第一个数组中遇到该数字时”的操作。这听起来像是线性搜索。
  • Hashtable 不是一个选项,因为在最坏的情况下它会花费你 O(n^2) 的时间。
  • 我的意思是,如果你做 array[currentNumber] 并且它是真的,这意味着第一个数组中存在相同的数字,因此你有你的解决方案。您没有搜索数组中的所有值,因为索引本身就是您要搜索的数字。
  • ...按照这个逻辑,所有算法都是 O(n),你只需要使用正确的 n 定义。您忽略的是 n 表示输入的长度。你不能选择它,因为输入决定了它。如果您不相信我,我建议您阅读 O-Notation;这显然超出了这个问题的范围。
  • @Neil:关键是您的数组大小为 O(n log n),而不是 O(n)。所以它需要 O(n log n) 来初始化你的数组。
【解决方案7】:

你试过counting sort吗?它实现简单,使用 O(n) 空间,并且具有 \theta(n) 时间复杂度。

【讨论】:

  • ... 对于长度不取决于输入大小的数字。但是在这里,随着输入变长,数字会变大......
  • 问题指出,“这些数组包含 0-n100 范围内的随机整数。”所以,我们知道数字的分布,所以计数排序是一种的选择。你是对的,如果数字是稀疏的,那么这种排序是对空间的巨大浪费。我只是提出了一个其他人还没有给出的想法。
【解决方案8】:

基于迄今为止发布的想法。我们可以将一个数组整数元素存储到哈希映射中。 RAM 中可以存储的不同整数的最大数量。哈希映射将只有唯一的整数值。重复项被忽略。

这里是 Perl 语言的实现。

#!/usr/bin/perl
use strict;
use warnings;
sub find_common_elements{ # function that prints common elements in two unsorted array
my (@arr1,@array2)=@_; # array elements assumed to be filled and passed as function arguments 

my $hash; # hash map to store value of one array

# runtime to prepare hash map is O(n). 
foreach my $ele ($arr1){
$hash->{$ele}=true; # true here element exists key is integer number and value is true,          duplicate elements will be overwritten

# size of array will fit in memory as duplicate integers are ignored   ( mx size will     be 2 ^( 32) -1 or 2^(64) -1 based on operating system) which can be stored in RAM.
} 
# O(n ) to traverse second array and finding common elements in two array
foreach my $ele2($arr2){
  # search in hash map is O(1), if all integers of array are same then hash map will have         only one entry and still search tim is O(1) 
   if( defined $hash->{$ele}){  
   print "\n $ele is common in both array \n";
   }
  }
 } 

希望对你有帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-10
    相关资源
    最近更新 更多