1. 简述
能否快速找出一个数组中的两个数字,让这两个数字之和等于一个给定的值,为了简化起见,我们假设这个数组中肯定存在至少一组符合要求的解。
2. 思路
方法一:遍历数组中任意两个数字,C(N,2)种组合,复杂度O(N^2)。
方法二:首先排序数组,然后遍历其中每个数字,二分查找找另一个符合条件的数字(即给定值减去遍历的当前数字),复杂度O(LogN + N*LogN),注意这个二分查找要忽略当前遍历的那个数字,注意修改二分查找的代码。另外计数排序或者哈希表也不错,这就不用排序了,复杂度可以到达O(N),另外需要空间复杂度至少为O(N)。
方法三:首先排序数组,假设先序排序,然后两边走,如果刚好两个数之和为给定值,那么即找到;如果小于给定值,那么左边向中间走一步;如果大于给定值,那么右边向中间走一步。复杂度O(N*LogN + N)。
3. 代码
给个方法三的代码:
void find_sum(const int *array, int N, int sum) {
assert(array != NULL);
int i,j;
i = 0;
j = N-1;
while(i < j) {
if(array[i]+array[j] < sum)
i++;
else if(array[i]+array[j] > sum)
j--;
else
break;
} // while
if(i < j)
cout << array[i] << "," array[j] << endl;
else
cout << "not found" << endl;
} // find_sum
4. 扩展
1) 如果把“两个数字”改为“三个数字”或“任意个数字”时,你的解是什么呢?
2) 如果完全相等的一对数字找不到,能否找出最接近的解?
3) 给定一个数N和一组数字集合S,求S中和最接近N的子集。
扩展问题,现在我的思路不多,第一个扩展网上有人说间接的递归来做,假设求K个数字,遍历N个数字,转化为K-1的情况,然后继续,直到N=2时,用前面的方法解决。不过这个思路的复杂度还是蛮高的,前面的递归过程就产生了C(N, K-2)种可能性,后面最快用计数排序或者哈希表也要N,那么就是N*C(N,K-2)。其他的没什么思路,以后再说了。
5. 参考
编程之美,2.12节,快速寻找满足条件的两个数
读书笔记之编程之美 - 2.12 快速寻找满足条件的两个数