题目:
两个数组,从每个数组中取一个数相加,求最大的前k个和数组A:1,2,3数组B:4,5,6则最大的前2个和:9,8。
方案1: naive method
把两个数组的和先求出来,然后用Nth-Elemnet,找出第k大的数。Nth-Element就是快排变形的那个partition。时间复杂度O(m*n),空间复杂度O(m*n)。
但是可以借助杨氏矩阵来优化这个方法。。。TODO
code略。
That can be easily done in O(k*logk). I'll only assume arrays are sorted in descending order, to simplify notation. The idea is simple. We'll find 1st, 2nd, .., k-th maximum values one by one. But to even consider pair (i, j) we need to have both (i-1, j) and (i, j-1) already selected, because they both are greater or equal than (i, j).
It's much like if we push all n*m pairs into the heap and then remove max k times.
void GetKthLargestSum(int arrA[], int lenA, int arrB[], int lenB, int k){
if (arrA == NULL || lenA == 0 || lenB == NULL || lenB == 0)
return;
// sort array A and B, descending order...
qsort(arrA, lenA, sizeof(int), compare);
qsort(arrB, lenB, sizeof(int), compare);
// key: sum(A, B), value: pair<indexOfA, indexOfB>
priority_queue<int, pair<int, int> > maxHeap;
int idxA = 0, idxB = 0;
maxHeap.push(pair<int, pair<int, int> >(arrA[0]+arrB[0], make_pair(idxA, idxB)));
for (int idx = 0; idx < k; ++idx){
pair<int, pair<int, int> > val = maxHeap.top();
cout << val.first << " ";
maxHeap.pop();
maxHeap.push(pair<int, pair<int, int> >(arrA[val.second.first+1]+arrB[val.second.second], make_pair(val.second.first+1, val.second.second)));
maxHeap.push(pair<int, pair<int, int> >(arrA[val.second.first]+arrB[val.second.second+1], make_pair(val.second.first, val.second.second+1)));
}
// TODO: 1. Duplicated pairs can be added to the heap, this can be prevented with hash.
// 2. Indexes need to be validated, e.g. that max.i + 1 < arrA.length.
}
方案3:近似O(n)方案
先用一个指针指向数组A的头,一个指向数组B的尾,如果A[0] + B[m] < C[k],A的指针后移,判断A[1] + B[m],如果A[0] + B[m] > C[k],则B的指针前移,判断A[0] + B[m-1]。简单说就是< C[k],A的指针后移,> C[k],B的指针前移。不过问题的关键在于计数,同时统计大于C[k]的小于C[k]的数有多少个。如果A[0] + B[m] < C[k],则A[0] + B[0],B[1]......B[m-1]都小于<C[k],小于的计数+m,如果A[0] + B[m] > C[k],则A[1],A[2]......A[n]+B[m]都大于C[k],大于计数+n,这样,就可以写出一个(m+n)*log(Max-Min)+ n*log(n) + m*log(m)的程序。
借鉴这篇文章"Selection in X + Y and matrices with sorted rows and columns" by A. Mirzaian and E. Arjomandi.
int qceil(int x)
{
return (x + 3) / 4;
}
int qhigh(int x, int n)
{
if (n & 1) // odd
return qceil(x + 2 * n + 1);
else // even
return n + 1 + qceil(x);
}
typedef std::vector<int> Vec;
struct Ranks
{
int ra_less;
int rb_more;
Vec l;
Ranks(const Vec& a1, const Vec& a2, int a, int b)
{
int n = a1.size() - 1;
ra_less = 0;
rb_more = n * n;
l.reserve(12 * n + 1);
l.push_back(0);
int j = n;
for (int i = 1; i <= n; ++i)
{
while (j && a1[i] + a2[j] <= a)
--j;
ra_less += j;
int jj = j;
while (jj && a1[i] + a2[jj] < b)
{
l.push_back(a1[i] + a2[jj]);
--jj;
}
rb_more -= jj;
}
}
};
Vec half(const Vec& a)
{
Vec res;
res.reserve(2 + (a.size() - 1) / 2);
res.push_back(0);
for (int i = 1; i < a.size(); i += 2)
res.push_back(a[i]);
if (a.size() & 1)
res.push_back(a[a.size() - 1]);
return res;
}
struct AB
{
int a;
int b;
};
AB biselect(const Vec& a1, const Vec& a2, int k1, int k2)
{
AB res;
int n = a1.size() - 1;
assert(n > 0);
if (n == 1)
{
res.a = res.b = a1[1] + a2[1];
}
else if (n == 2)
{
Vec l {a1[1] + a2[1], a1[1] + a2[2], a1[2] + a2[1], a1[2] + a2[2]};
std::nth_element(l.begin(),
l.end() - k1,
l.end());
res.a = l[4 - k1];
std::nth_element(l.begin(),
l.end() - k2,
l.end());
res.b = l[4 - k2];
}
else
{
int k1_half = qhigh(k1, n);
int k2_half = qceil(k2);
AB ab = biselect(half(a1), half(a2), k1_half, k2_half);
Ranks ranks(a1, a2, ab.a, ab.b);
int r1 = k1 + ranks.rb_more - n * n;
int r2 = k2 + ranks.rb_more - n * n;
if (ranks.ra_less <= k1 - 1)
res.a = ab.a;
else if (r1 <= 0)
res.a = ab.b;
else
{
std::nth_element(ranks.l.begin() + 1,
ranks.l.end() - r1,
ranks.l.end());
res.a = *(ranks.l.end() - r1);
}
if (ranks.ra_less <= k2 - 1)
res.b = ab.a;
else if (r2 <= 0)
res.b = ab.b;
else
{
std::nth_element(ranks.l.begin() + 1,
ranks.l.end() - r2,
ranks.l.end());
res.b = *(ranks.l.end() - r2);
}
}
return res;
}
int select(const Vec& a1, const Vec& a2, int k)
{
assert(a1.size() == a2.size());
AB ab = biselect(a1, a2, k, k);
assert(ab.a == ab.b);
return ab.a;
}
int check(const Vec& a1, const Vec& a2, int k)
{
int n = a1.size() - 1;
Vec sum;
sum.reserve(n * n);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
sum.push_back(a1[i] + a2[j]);
std::nth_element(sum.begin(),
sum.end() - k,
sum.end());
return *(sum.end() - k);
}
#include <random>
int sovle_two_arrays_sum()
{
std::random_device rd;
std::default_random_engine e1(rd());
std::uniform_int_distribution<int> uniform_dist(1, 6);
int n = 100000;
Vec a1, a2;
a1.reserve(n+1);
a2.reserve(n+1);
int x = 1000;
a1.push_back(0);
for (int i = 0; i < n; ++i)
{
x -= uniform_dist(e1);
a1.push_back(x);
}
x = 1000;
a2.push_back(0);
for (int i = 0; i < n; ++i)
{
x -= uniform_dist(e1);
a2.push_back(x);
}
std::cout << select(a1, a2, 1 + n * n / 7) << '\n'
/*<< check(a1, a2, 1 + n * n / 7) << '\n'*/;
return 0;
}
Ref:
http://stackoverflow.com/questions/18557175/how-to-find-pair-with-kth-largest-sum
http://www.51nod.com/question/index.html#!questionId=46