给定两个排序的数字数组,我们希望找到具有第k个最大可能总和的对。 (一对是第一个数组中的一个元素,第二个数组中是一个元素)。例如,使用数组
总和最大的对是
所以第四大总和是(13,8)。如何找到具有第k个最大可能总和的对?
我正在寻找一个涉及最小堆或最大堆的解决方案。
答案 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;
}