题目1:谷歌面试题——输入是两个整数数组,他们任意两个数的和又可以组成一个数组,求这个和中前k个数怎么取?
分析:
“假设两个整数数组为A和B,各有N个元素,任意两个数的和组成的数组C有N^2个元素。
那么可以把这些和看成N个有序数列:
A[1]+B[1] <= A[1]+B[2] <= A[1]+B[3] <=…
A[2]+B[1] <= A[2]+B[2] <= A[2]+B[3] <=…
…
A[N]+B[1] <= A[N]+B[2] <= A[N]+B[3] <=…
问题转变成,在这N^2个有序数列里,找到前k小的元素”
题目2:有两个序列A和B,A=(a1,a2,...,ak),B=(b1,b2,...,bk),A和B都按升序排列。对于1<=i,j<=k,求k个最小的(ai+bj)。要求算法尽量高效。
分析:
设定两个下标i,j分别指向A,B的尾部,根据A[i],B[j]两个元素的大小分别移动相应的下标,直到(i-1)j<k或(j-1)i<k,此时剩下的组合数为i*j,遍历数组求得前k个最小和,返回给用户。
参考代码:
#include <iostream>
using namespace std;
int *min_k(int *A, int *B, int len1, int len2, int k)
{
//非法输入
if(A == NULL || B == NULL || k <= 0)
return NULL;
int *tmp = new int[k];
int i = len1, j = len2;
//找出剩下组合k个元素
while(i > 0 && j > 0)
{
if(A[i-1] > B[j-1])
{
if((i-1)*j >= k)
i--;
else
break;
}
else
{
if((j-1)*i >= k)
j--;
else
break;
}
}
//遍历数组求出前k个最小和
int count = 0;
if(A[i-1] > B[j-1])
{
int p,q;
for(p = 0;p < i;p++)
{
for(q = 0;q < j;q++)
{
if(count < k)
tmp[count++] = A[p]+ B[q];
else
break;
}
}
}
else
{
int p,q;
for(p = 0;p < j;p++)
{
for(q = 0;q < i;q++)
{
if(count < k)
tmp[count++] = B[p] + A[q];
else
break;
}
}
}
return tmp;
}
int main()
{
int len1,len2,k;
cin >> len1 >> len2 >> k;
int *A = new int[len1];
int *B = new int[len2];
for(int i = 0;i < len1;i++)
cin >> A[i];
for(int j = 0;j < len2;j++)
cin >> B[j];
int *target = min_k(A,B,len1,len2,k);
for(int i = 0;i < k; i++)
cout << target[i] << " ";
cout << endl;
delete []A;
delete []B;
delete []target;
return 0;
}
调试中需要注意的地方:
1)熟悉如何初始化int *A = new int[len1]
2)区分下delete A和delete []A
delete 释放new分配的单个对象指针指向的内存
delete[] 释放new分配的对象数组指针指向的内存
在内置的类型中,比如int,char,long等delete 和 delte[]是没有区别的,但是如果是自定义的结构或类,则不会回收,就会造成内存泄露
3)该程序还有点问题,当输入为1 5 6和2 5 5输出结果不对。有所欠缺
4)时间复杂度为min{O(min(len1,len2)),O(k)},空间复杂度为O(k),若只是需要输出最小的k个和,则不需要用O(k)的空间把k个最小和存储起来,这样时间复杂度为O(1).
3、给定一个数列a1,a2,a3,...,an和m个三元组表示的查询,对于每个查询(i,j,k),输出ai,ai+1,...,aj的升序排列中第k个数。
(待补充)