给定两个数组a和b。找出所有元素对(a1,b1),使得a1属于数组A,b1属于数组B,其总和a1 + b1 = k

时间:2010-09-28 16:51:59

标签: algorithm set

我正在寻找具有最小时间和空间复杂度的以下算法的解决方案。

  

给定两个数组a和b,找到所有元素对(a1,b1),使得a1属于数组A,b1属于数组B,其总和a1 + b1 = k(任何整数)。

我能够提出O(n log n)方法,我们将其中一个数组排序为A,对于数组B中的每个元素b,对排序数组A进行二进制搜索以获得值(Kb)

我们可以进一步改进吗?

5 个答案:

答案 0 :(得分:18)

如果对数组进行了排序,则可以在线性时间和常量存储中进行排序。

  • 从两个指针开始,一个指向A的最小元素,另一个指向B的最大元素。
  • 计算指向元素的总和。
  • 如果小于k,则将指针增加到A,使其指向下一个最大元素。
  • 如果大于k,则将指针递减到B,使其指向下一个最小元素。
  • 如果确切地说是k,你就找到了一对。移动其中一个指针并继续寻找下一对。

如果数组最初是未排序的,那么您可以先对它们进行排序,然后使用上面的算法。根据您期望的数据类型,可以使用几种不同的方法对它们进行排序:

比较排序平均需要O(n log n)时间。最后两个比O(n log(n))快,但如果输入数组中可能值的范围非常大,则可能不切实际。

答案 1 :(得分:10)

如果您对最大可能数量有限制(让我们将其命名为M),那么您可以在O(M + n)中找到解决方案。

false的布尔数组和标记为真的A元素的所有值。然后,对于B的每个元素b,检查元素编号K-b是否标记为真。

如果您使用的是哈希映射而不是大数组,则可以改进它。但我不会认为在这种问题中,哈希映射是一种欺骗行为。

无论如何,它会给你O(n)插入,然后O(n)用于查询,O(n)总计。

编辑:

这可能有用的一种情况。

  • 您有未分类的大小为10 ^ 6的向量,因此对它们进行排序并进行匹配是在O(n log n)中,n = 10 ^ 6.
  • 你需要做10到6次这个操作(不同的向量),复杂度为O(n * n * log n)。
  • 最大值为10 ^ 9。

使用我的想法不是布尔而是整数(每次运行时增加)会给你一个复杂的:

  • “O(10 ^ 9)”创建数组(也是相同的空间复杂度)
  • 每次运行时
  • O(n),所以O(n * n)为总计。

你正在使用更多的空间,但在这种情况下你的速度提高了一个因子log(n)〜= 20!

答案 2 :(得分:3)

我会创建一个包含一个数组元素的哈希表,然后迭代另一个查找k - a(n)的数组,如果查找成功则生成一个输出元素。这将使用O(n)空格和时间。

在C#中,它可能如下所示:

var bSet = new HashSet(B);
var results = from a in A
              let b = k - a
              where bSet.Contains(b)
              select new { a, b };

答案 3 :(得分:1)

template< typename T >
std::vector< std::pair< T, T > >
find_pairs( 
    std::vector< T > const & a, std::vector< T > const & b, T const & k  ) {

    std::vector< std::pair< T, T > > matches;

    std::sort( a.begin(), a.end() );  // O( A * lg A )
    std::sort( b.begin(), b.end() );  // O( B * lg B )

    typename std::vector< T >::const_iterator acit = a.begin();
    typename std::vector< T >::const_reverse_iterator bcit = b.rbegin();

    for( ; acit != a.end(); /* inside */ ) {
        for( ; bcit != b.rend(); /* inside */ ) {

            const T sum = *acit + *bcit;

            if( sum == k ) {
                matches.push_back( std::pair< T, T >( *acit, *bcit ) );
                ++acit;
                ++bcit;
            }
            else if( sum < k ) {
                ++acit;
            }
            else {  // sum > k
                ++bcit;
            }
        }
    }  // O( A + B )
    return matches;
}

答案 4 :(得分:0)

我使用了C ++,它似乎给了我想要的结果。希望这是你想要的。

using namespace std;

using data=std::pair<int,int>;

void search_pairs(std::vector<int>& A, std::vector<int>& B, const int total, std::vector<data>& output){

  std::sort(A.begin(),A.end(),[](const int i,const int j)->bool{return (i<j);});
  std::sort(B.begin(),B.end(),[](const int a,const int b)->bool{return (a<b);});
  std::vector<int>* minV(nullptr);
  std::vector<int>* maxV(nullptr);
  if(A.size()>B.size()) {minV=&B;maxV=&A;}
  else {minV=&A;maxV=&B;}
  for(auto&& itr:(*minV) ){
    auto remain(total-itr);
    if (std::binary_search (maxV->begin(), maxV->end(), remain)){
      data d{itr,remain};
      if (minV==&B) std::swap(d.first,d.second);
      output.push_back(d);
    }
  }
  if (minV==&B) std::reverse(output.begin(),output.end());  
}

int main() {

    size_t nb(0);
    scanf("%lu",&nb);
    for (size_t i=0;i<nb;++i){
        size_t a,b(0);
        int total(0);
        scanf("%lu %lu %d",&a,&b,&total);
        std::vector<int> A,B;
        for (size_t i=0;i<a;++i){
            int aux;
            scanf("%d",&aux);
            A.push_back(aux);
        } 
        for (size_t i=0;i<b;++i){
            int aux;
            scanf("%d",&aux);
            B.push_back(aux);
        } 
        std::vector<data> output;
        search_pairs(A, B, total, output);
    auto itr=std::begin(output);
    if (itr==std::end(output)) printf("-1"); 
    while (itr!=std::end(output)){
      printf("%d %d",(*itr).first, (*itr).second);
      if (++itr!=std::end(output)) printf(", ");
    }
        printf("\n");
    }
    //code
    return 0;
}