求两个有序数组两两相加的值最小的K个数

周五面试百度,面试题

搜索原题在这


题目描述:

有两个大小为N的有序数组a、b(从大到小),现在需要找出K个最大的数字,其中每个数字是这样构成的(ai+bj),0<=i,j


例1:

Input:

a为7 6 5 4 

b为4 3 2 1

k=4

Output:11 10 10 9


例2:

Input:

a为7 4 3 2

b为6 3 1 1

k=6

Output:13 10 10 9 8 8


思路:

看到Top k,会想到利用最大堆来降低复杂度。

思路一:暴力方法就是计算所有a[i]+b[j],然后排序找出Top k。

思路二:其实所有的a[i]+b[j]构成一二维数组,我们不妨将这个二维数组写出来如下:

-a[0]+b[0]>=a[0]+b[1]>=...>=a[0]+b[n-2]>=a[0]+b[n-1]

-a[1]+b[0]>=a[1]+b[1]>=...>=a[1]+b[n-2]>=a[1]+b[n-1]

-...

-a[n-1]+b[0]>=a[n-1]+b[1]>=...>=a[n-1]+b[n-2]>=a[n-1]+b[n-1]

看到这个二维数组,立刻想到两点:

  • 方法一对n*n个数字排序没有考虑这些数字本身有一定的序关系
  • 归并排序,多路归并。利用上述数组中最右边一列构造一个最大堆。pop最大元素,然后将对应行的下一个元素加入到堆中。如此循环k次便可找到Top k。

思路一代码:
空间O(m*n),这里空间实际上可以不要。直接在二重循环下建堆入堆更新。
时间O(mnlog(mn)),这里采用堆的话,时间O(mnlog(k))
vector getTopKFromTwoArraysForce(int arr1[], int m, int arr2[], int n, int k){
	vector res;
	if(arr1==NULL || m<=0 || arr2==NULL || n<=0 || k<=0)
		return res;

	vector candidate;
	candidate.reserve(m*n);
	for (int i=0; i());
	copy(candidate.begin(), candidate.begin()+k, back_inserter(res));
	return res;
}

思路二代码(参考这里):
空间:O(n)
时间:O(klog(n))
struct node{
	int i;   // arr1的下标
	int n;   // 2维数组中的元素值 arr1[i] + arr2[j]
};
// 优先级队列的比较函数
struct cmp{
	bool operator() (const node& a, const node& b) {
		return a.n < b.n;
	}
};

vector getTopKFromTwoArrays(int arr1[], int m, int arr2[], int n, int k){
	vector res;
	if(arr1==NULL || m<=0 || arr2==NULL || n<=0 || k<=0)
		return res;

	int *d = new int[m];
	for(int i=0; i, cmp> Q; //优先级队列
	for(int i=0; i


回顾
  • 一般top问题可以考虑利用堆降低时间复杂度
  • 多路归并
  • 数组问题多挖掘已知隐含信息
  • STL的东西很好用,好好学学
  • 上述代码未考虑m,n,k大小关系,会有异常
  • 扩展:2个有序数组扩展为多个有序数组,思路类似




你可能感兴趣的:(算法与数据结构)