在第2个数组中找到具有第k个最大和的对

时间:2011-03-06 17:16:52

标签: algorithm sorting

给定两个排序的数字数组,我们希望找到具有第k个最大可能总和的对。 (一对是第一个数组中的一个元素,第二个数组中是一个元素)。例如,使用数组

  • [2,3,5,8,13]
  • [4,8,12,16]

总和最大的对是

  • 13 + 16 = 29
  • 13 + 12 = 25
  • 8 + 16 = 24
  • 13 + 8 = 21
  • 8 + 12 = 20

所以第四大总和是(13,8)。如何找到具有第k个最大可能总和的对?

我正在寻找一个涉及最小堆或最大堆的解决方案。

3 个答案:

答案 0 :(得分:24)

可以在O(k*logk)轻松完成。我只假设数组按降序排序,以简化表示法。

这个想法很简单。我们将逐个找到第1,第2,......,第k个最大值。但是,即使考虑对(i, j),我们也需要选择(i-1, j)(i, j-1),因为它们都大于或等于(i, j)

就像我们将所有n*m对推入堆中然后删除最多k次一样。只有我们不需要所有n*m对。

heap.add(pair(0, 0));  // biggest pair

// remove max k-1 times
for (int i = 0; i < k - 1; ++i) {
    // get max and remove it from the heap
    max = heap.popMax();

    // add next candidates
    heap.add(pair(max.i + 1, max.j));
    heap.add(pair(max.i, max.j + 1));
}

// get k-th maximum element
max = heap.popMax();
maxVal = a[max.i] + b[max.j];

需要考虑的一些事项。

  • 可以将重复的对添加到堆中,这可以通过哈希来防止。
  • 需要验证索引,例如那max.i + 1 < a.length

答案 1 :(得分:1)

我理解你想要一堆,但这并不是最有效的解决方案,正如phimuemue指出的那样。

你可以max_heap这两个数组,并在两者的根目录下设置迭代器。此时,您的金额最大。

现在,在每个步骤中,找到两个指针的子节点和邻居中最大的,未访问的节点 - 这是下一个最大的总和。将指针移动到相应的堆中。

重复k次。

答案 2 :(得分:1)

这是我的答案,我认为它运作良好,但有人可以告诉我它的复杂性是什么?

由于

int ksum( int a[], int n, int b[], int m, int maxk )
{

    std::vector<int> results;
    int* check = new int[m*n];
    memset( check, 0, n*m*sizeof(int) );

    int finali, finalj;
    int starti = 0, startj = 0;
    for( int k=1; k<maxk+1; ++k )
    {
        int max = 0;
        int maxi=n, maxj=m;
        for( int i=starti; i<n && i<k; ++i )
        {
            if( i>maxj )
                break;
            for( int j=i; j<m && j<k; ++j )
            {
                if( i>maxi && j>=maxj )
                    break;
                if( check[i*m+j] == 0 )
                {
                    int tmp = a[i]+b[j];
                    if( tmp>max )
                    {
                        max = tmp;
                        finali = i, finalj = j;
                    }
                    maxi = i, maxj = j;
                    break;
                }
            }
        }
        starti = finali;

        maxi=n, maxj=m;
        for( int j=startj; j<n && j<k; ++j )
        {
            if( j>maxi )
                break;
            for( int i=j; i<m && i<k; ++i )
            {
                if( j>maxj && i>=maxi )
                    break;
                if( check[i*m+j] == 0 )
                {
                    int tmp = a[i]+b[j];
                    if( tmp>max )
                    {
                        max = tmp;
                        finali = i, finalj = j;
                    }
                    maxi = i, maxj = j;
                    break;
                }
            }
        }
        startj = finalj;

        if( max > 0 )
        {
            check[finali*m+finalj] = 1;
            results.push_back( max );
        }
    }

    delete[] check;
    if( maxk > results.size() )
        return 0;
    else
        return results[maxk-1];
}

int _tmain(int argc, _TCHAR* argv[])
{
    int a[] = {10,8,6,4,1};
    int b[] = {9,6,3,2,1};
    int res = ksum( a, 5, b, 5, 9 );
    return 0;
}