我们需要在数组中找到一对数,其总和等于给定值 .
A = {6,4,5,7,9,1,2}
Sum = 10然后对是 - {6,4},{9,1}
我有两个解决方案 .
-
一个O(nlogn)解决方案 - 用2个迭代器(开始和结束)排序校验和 .
-
一个O(n)解决方案 - 散列数组 . 然后检查哈希表中是否存在
sum-hash[i]
.
但是,问题在于虽然第二种解决方案是O(n)时间,但也使用O(n)空间 .
所以,我想知道我们是否可以在 O(n) 时间和 O(1) 空间内完成 . 这不是功课!
18 回答
如果数组中的两个整数与比较整数匹配,则以下代码返回true .
下面的代码采用数组和数字N作为目标总和 . 首先对数组进行排序,然后获取包含剩余元素的新数组,然后不通过二进制搜索扫描,而是同时扫描剩余部分和数组 .
不应该从两端迭代才能解决问题?
对数组进行排序 . 并从两端开始比较 .
时间复杂度
O(nlogn)
如果它是一个 sorted 数组并且我们只需要一对数字而不是所有对,我们就可以这样做:
1 2 3 9 11 20 || i = 0,j = 5 sum = 21 x = 11
1 2 3 9 11 20 || i = 0,j = 4 sum = 13 x = 11
1 2 3 9 11 20 || i = 0,j = 4 sum = 11 x = 11
结束
使用就地基数排序和OP的第一个解决方案与2个迭代器相互接近 .
如果数组中的数字不是某种多精度数字并且例如是32位整数,则可以使用几乎没有额外空间(每次传递1位)在2 * 32次传递中对它们进行排序 . 或者2 * 8遍和16个整数计数器(每遍4位) .
Details for the 2 iterators solution:
第一个迭代器最初指向排序数组的第一个元素并向前推进 . 第二个迭代器最初指向数组的最后一个元素并向后前进 .
如果迭代器引用的元素总和小于所需的值,则前进第一个迭代器 . 如果它大于所需的值,则前进第二个迭代器 . 如果它等于所需的值,则成功 .
只需要一次传递,因此时间复杂度为O(n) . 空间复杂度为O(1) . 如果使用基数排序,整个算法的复杂性是相同的 .
如果您对相关问题感兴趣(总和超过2个数字),请参阅"Sum-subset with a fixed subset size"和"Finding three elements in an array whose sum is closest to an given number" .
这是来自微软亚洲研究院的经典访谈问题 .
如何在未排序的数组中查找等于给定总和的2个数字 .
[1]蛮力解决方案
这个算法很简单 . 时间复杂度为O(N ^ 2)
[2]使用二进制搜索
使用bianry搜索找到每个arr [i]的Sum-arr [i],时间复杂度可以减少到O(N * logN)
[3]使用哈希
基于[2]算法并使用哈希,时间复杂度可以减少到O(N),但是这个解决方案将添加哈希的O(N)空间 .
[4]最优算法:
Pseduo代码:
要么
而且,这个问题完全解决了吗?否 . 如果数字是N.这个问题将变得非常复杂 .
问题然后:
如何找到具有给定数字的所有组合案例?
这是一个经典的NP-Complete问题,称为subset-sum .
要了解NP / NPC / NP-Hard,您最好阅读一些专业书籍 .
参考文献:
[1] http://www.quora.com/Mathematics/How-can-I-find-all-the-combination-cases-with-a-given-number
[2] http://en.wikipedia.org/wiki/Subset_sum_problem
如果您假设对假设求和的值
M
是常量并且数组中的条目是正数,那么您可以使用M/2
指针(O(1)
空格)在一次传递(O(n)
时间)中执行此操作,如下所示 . 指针标记为P1,P2,...,Pk
,其中k=floor(M/2)
. 然后做这样的事情例如,您可以通过将索引存储为base
N
中的数字来处理重复的条目(例如,两个6) . 对于M/2
,您可以添加条件但是现在你有把这些配对放在一起的问题 .
明显的解决方案不起作用(迭代每个连续的对)或者是任何顺序的两个数字吗?
在这种情况下,您可以对数字列表进行排序,并使用随机抽样对已排序的列表进行分区,直到您有一个足够小的子列表进行迭代 .
Python 2.7实现:
输出:
https://github.com/clockzhong/findSumPairNumber
我已经成功地在O(n m)时间和空间成本下使用Python实现了一个解决方案 . “m”表示这两个数和的总和所需的目标值 . 我相信这是最低的成本 . Erict2k使用了itertools.combinations,与我的算法相比,它也会花费相似或更高的时间和空间成本 .
如果数字不是很大,你可以使用快速傅立叶变换乘以两个多项式,然后在O(1)中检查x ^(所需总和)和之前的系数是否大于零 . O(n log n)总计!
//使用Hashing导入java.io. *的Java实现;
class PairSum {private static final int MAX = 100000; // Hashmap的最大大小
}
使用对键(列表中的数字)创建字典,值是获取所需值所需的数字 . 接下来,检查列表中是否存在数字对 .
版本2