查找两组相交的算法

时间:2008-10-29 01:56:16

标签: algorithm language-agnostic

假设我有两个阵列:

  

int ArrayA [] = {5,17,150,230,285};

     

int ArrayB [] = {7,11,57,110,230,250};

两个数组都已排序,可以是任何大小。我正在寻找一种有效的算法来查找数组是否包含它们之间的任何重复元素。我只想要一个真/假答案,我不关心共享哪个元素或多少元素。

天真的解决方案是循环遍历ArrayA中的每个项目,并在ArrayB中为它执行binary search。我相信这种复杂性是O(m * log n)。

因为两个数组都已排序,所以似乎应该有更高效的算法。

我还想要一个通用的解决方案,它不假设数组包含数字(即解决方案也适用于字符串)。但是,比较运算符定义良好,两个数组都从最小到最大排序。

7 个答案:

答案 0 :(得分:39)

假装您正在进行合并,但不要将结果发送到任何地方。如果到达任一源的末尾,则没有交集。每次比较每个元素的下一个元素时,如果它们相等,就会有一个交集。

例如:

counterA = 0;
counterB = 0;
for(;;) {
    if(counterA == ArrayA.length || counterB == ArrayB.length)
        return false;
    else if(ArrayA[counterA] == ArrayB[counterB])
        return true;
    else if(ArrayA[counterA] < ArrayB[counterB])
        counterA++;
    else if(ArrayA[counterA] > ArrayB[counterB])
        counterB++;
    else
        halt_and_catch_fire();
}

答案 1 :(得分:7)

因为有人想知道stl。开箱即用的set_intersection算法会比你想要的更多:它会找到所有常见的值。

    #include <vector>
    #include <algorithm>
    #include <iterator>
    using namespace std;
//    ...    
      int ArrayA[] = {5, 17, 150, 230, 285};
      int ArrayB[] = {7, 11, 57, 110, 230, 250};
      vector<int> intersection;
      ThrowWhenWritten output_iterator;
        set_intersection(ArrayA, ArrayA + sizeof(ArrayA)/sizeof(int),
                         ArrayB, ArrayB + sizeof(ArrayB)/sizeof(int),
                         back_insert_iterator<vector<int> >(intersection));

        return !intersection.empty();

这在O(m + n)时间运行,但它需要存储所有重复项,并且在找到第一个dup时不会停止。

现在,修改stl的gnu implementation中的代码,我们可以更准确地得到你想要的内容。

 template<typename InputIterator1, typename InputIterator2>
 bool 
 has_intersection(InputIterator1 first1, InputIterator1 last1,
             InputIterator2 first2, InputIterator2 last2)
    {
       while (first1 != last1 && first2 != last2) 
       {
          if (*first1 < *first2)
             ++first1;
          else if (*first2 < *first1)
             ++first2;
          else
             return true;
       }
       return false;
}

答案 2 :(得分:4)

如果一个列表比另一个列表短得多,那么二进制搜索就是最佳选择。如果列表具有相似的长度并且您对O(m + n)感到满意,则标准的“合并”将起作用。有更灵活的算法更灵活。我在自己的搜索中遇到的一篇论文是:

http://www.cs.uwaterloo.ca/~ajsaling/papers/paper-spire.pdf

答案 3 :(得分:3)

如果你不关心内存消耗,你可以通过使用hash来获得良好的性能,即使用keys =一个数组的值创建哈希值,并针对此哈希值测试第二个数组的值

答案 4 :(得分:1)

如果您使用的是C#3.0,那么为什么不在这里利用LINQ?

ArrayA.Intersect(ArrayB).Any()

这种通用(适用于任何类似的类型)不仅非常有效(使用散列算法)。

答案 5 :(得分:0)

如果值的范围很小,您可以为其中一个构建查找表(时间成本= O(N)),然后检查该位是否从另一个列表中设置(时间成本= O(N) )。如果范围很大,您可以使用哈希表执行类似操作。

来自Glomek的合并技巧是一个更好的主意。

答案 6 :(得分:0)

Glomek走在正确的轨道上,但有点掩盖了算法。

首先将ArrayA [0]与ArrayB [0]进行比较。如果他们是平等的,你就完成了。 如果ArrayA [0]小于ArrayB [0],则移至ArrayA [1]。 如果ArrayA [0]超过ArrayB [0],则移至ArrayB [1]。

保持踩踏直到你到达一个阵列的末尾或找到匹配。