Algorithm 求两个集合是否相交的算法

Algorithm 求两个集合是否相交的算法,algorithm,language-agnostic,Algorithm,Language Agnostic,假设我有两个数组: int ArrayA[]={5,17,150,230,285} int ArrayB[]={7,11,57,110,230,250} 两个数组都已排序,可以是任意大小。我正在寻找一种有效的算法,以确定数组之间是否包含任何重复的元素。我只想要一个正确/错误的答案,我不在乎哪个元素是共享的,或者有多少个元素是共享的 简单的解决方案是循环遍历ArrayA中的每个项,并在ArrayB中为其执行一个任务。我相信这个复杂性是O(m*logn) 因为两个数组都是排序的,所以似乎应该有一个更

假设我有两个数组:

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

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

两个数组都已排序,可以是任意大小。我正在寻找一种有效的算法,以确定数组之间是否包含任何重复的元素。我只想要一个正确/错误的答案,我不在乎哪个元素是共享的,或者有多少个元素是共享的

简单的解决方案是循环遍历ArrayA中的每个项,并在ArrayB中为其执行一个任务。我相信这个复杂性是O(m*logn)

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


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

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

例如:

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();
}
计数器A=0;
计数器B=0;
对于(;;){
如果(计数器A==ArrayA.length | |计数器B==ArrayB.length)
返回false;
else if(ArrayA[counterA]==ArrayB[counterB])
返回true;
否则如果(ArrayA[counterA]ArrayB[counterB])
计数器B++;
其他的
停下来,然后着火;
}

如果您不关心内存消耗,您可以通过使用散列来实现良好的性能,即使用键=一个数组的值创建散列,并根据该散列测试第二个数组的值

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


Glomek的合并排序技巧是一个更好的主意。

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

首先比较ArrayA[0]和ArrayB[0]。如果他们相等,你就完了。 如果ArrayA[0]小于ArrayB[0],则移动到ArrayA[1]。 如果ArrayA[0]大于ArrayB[0],则移动到ArrayB[1]

继续单步执行,直到到达一个数组的末尾或找到匹配项。

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

ArrayA.Intersect(ArrayB).Any()

这不仅是通用的(适用于任何类似的类型),而且引擎盖下的实现非常有效(使用哈希算法)

因为有人想知道stl。开箱即用的set_交集算法将比您想要的做得更多:它将找到所有公共值

    #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();
#包括
#包括stl,我们可以更准确地得到您想要的

 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;
}
模板
布尔
具有\u交叉点(InputIterator1 first1,InputIterator1 last1,
输入过滤器2优先2,输入过滤器2最后2)
{
while(first1!=last1&&first2!=last2)
{
如果(*first1<*first2)
++第一个1;
否则如果(*first2<*first1)
++前2名;
其他的
返回true;
}
返回false;
}

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


对两个数组中较小的数组进行哈希运算,以节省最多内存。这个解决方案肯定会非常快。顺便说一句,我们说你在这里概述的解决方案的复杂性是O(m*logn),其中m和n是两个数组的大小。我有一种感觉是这样的。谢谢。如果不明显,这个解决方案是O(n)BTW,这将对C++泛型器的通用代码很有用。这让我觉得STL应该已经提供了一个解决方案……一个诡辩:我鄙视无限循环。这应该是一个“while(counterA!=ArrayA.length&&counterB!=ArrayB.length)”(去掉第一个if())而不是一个“for(;)”,实际上,它是O(n+m),没有办法解决这个问题。考虑数组[1,2,3…99, 100 ]和〔50, 101〕。在终止之前,它必须查看所有102个数组元素。次要的诡辩:O(n)==O(m+n)-大-O表示法用于,而不是绝对度量。O(n)简单地说,该算法是线性的——您将在每个元素上迭代一次。n的大小无关紧要。很好很简单,虽然我不会使用您从GNU复制的名称,但STL实现允许使用这些符号,但POD(普通的老开发人员)不允许(双下划线和下划线大写在实现中被解析)。