问题:有两个单调递增的数组 a[n] 和 b[m],这两个数组的和值 a[i]+b[j],其中 i=1, ..., n,j=1, ..., m,共有 n*m 个,要求返回前 k 项最小的和。例如:a[4] = {-5, 2, 7, 14},b[4]={-3, -1, 15, 21} 时,要求返回前 8 个时为:-8, -6, -1, 1, 4, 6, 10, 11。
具体的程序如下:
#include <stdio.h>
#include <stdlib.h>
#define max(a,b) ((a)>=(b) ? (a) : (b))
int a[4] = {-5, 2, 7, 14};
int b[4] = {-3, -1, 15, 21};
int n = sizeof(a)/sizeof(a[0]); //数组a的长度
int m = sizeof(b)/sizeof(b[0]); //数组b的长度
struct point {
int x;
int y;
};
int ptcmp(struct point pt1, struct point pt2)
{
return a[pt1.x]+b[pt1.y]-a[pt2.x]-b[pt2.y];
}
void minheapify(struct point *arr, int i, int len)
{
int least, l, r;
struct point temppt;
l = 2*i + 1;
r = 2*i + 2;
if (len>1) {
if (l < len && ptcmp(arr[l], arr[i]) < 0)
least = l;
else
least = i;
if (r < len && ptcmp(arr[r], arr[least]) < 0)
least = r;
if (least != i) {
temppt = arr[i];
arr[i] = arr[least];
arr[least] = temppt;
minheapify(arr, least, len);
}
}
}
struct point heapexactmin(struct point *arr,int *lenpt)
{
struct point temppt;
if (*lenpt < 1) {
printf("heap underflow\n");
exit(1);
}
//交换数组的第一个元素和最后一个元素
temppt = arr[0];
arr[0] = arr[*lenpt-1];
arr[*lenpt-1] = temppt;
*lenpt = *lenpt - 1;
//保持最小堆
minheapify(arr, 0, *lenpt);
return arr[*lenpt];
}
void heapinsert(struct point *arr, int *lenpt, struct point newpt)
{
int i;
struct point temppt;
arr[*lenpt] = newpt;
*lenpt = *lenpt + 1;
i = *lenpt - 1;
while ((i-1)/2 >= 0 && ptcmp(arr[i], arr[(i-1)/2]) < 0) {
temppt = arr[i];
arr[i] = arr[(i-1)/2];
arr[(i-1)/2] = temppt;
i = (i-1)/2;
}
}
int main()
{
int candnum;
struct point *cand;
struct point minpt, rpt, dpt;
int i, count, k;
int *kminsum;
printf("Please enter a positive integer number no more than %d for variable k:\n", m*n);
scanf("%d", &k);
kminsum = (int *) malloc(k*sizeof(int));
cand = (struct point *) malloc(max(n,m)*sizeof(struct point));
//初始化 cand 数组
cand[0].x = 0;
cand[0].y = 0;
candnum = 1;
count = 0;
while (count < k) {
minpt = heapexactmin(cand, &candnum);
kminsum[count] = a[minpt.x] + b[minpt.y];
//判断 minpt 右边的点能否加进 cand 数组
rpt.x = minpt.x;
rpt.y = minpt.y+1;
if (rpt.y < m) {
for (i=0; i<candnum && (rpt.x != cand[i].x || rpt.y != cand[i].y); i++)
;
if (i >= candnum)
heapinsert(cand, &candnum, rpt);
}
//判断 minpt 下边的点能否加进 cand 数组
dpt.x = minpt.x+1;
dpt.y = minpt.y;
if (dpt.x < n) {
for (i=0; i<candnum && (dpt.x != cand[i].x || dpt.y != cand[i].y); i++)
;
if (i >= candnum)
heapinsert(cand, &candnum, dpt);
}
count++;
}
for (count = 0; count < k; count++)
printf("%d ", kminsum[count]);
printf("%\n");
return 0;
}
注:在这里我用到了堆的相关操作,其实是不必要的。只不过是我想练习一下堆的相关操作而已。从 main 函数中可以看出算法的思想很简单。但是我本人对这个算法不是很满意,希望能找到更好的算法。