双数和(Two Sum)
2015-01-23 18:51 coding-pig 阅读(293) 评论(0) 收藏 举报给定一个整数数列,找出两个数,使其和等于给定值K,并返回其下标。如果无法找到两个数,返回(-1,-1)。
经典面试问题。主要考察设计算法是对时间和空间复杂度的把握。
首先一些基本思考:
- 问题的边界条件是什么?数据有何特点?数据量有多大?
- 最简单的暴力解法是什么?进一步优化的途径何在?
首先该数组可能存在重复元素,如(3,1,1,4,2)。如果K值设定为3,则可能返回的结果为(1,4)或者(2,4)。注意我们要返回的是下标。由于原题没有指明返回符合条件的多个可能解的限制条件,顾返回任何一对下标都是接受的。数据元素为整数。题目没有说明数组的长度,故先可假定数组可放入内存,而先无须顾虑用序列化,分布式或并行解法。
最简单的暴力解法是穷举所有不同元素对,当发现其和等于K时,返回该元素对的下标。如果没找到满足条件的元素对,则返回(-1,-1)。Python代码如下:
1 def find_two_sum(values, K): 2 if len(values) >= 2: 3 for i in range(len(values)-1): 4 for j in range(i+1, len(values)): 5 if values[i] + values[j] == K: 6 return (i,j) 7 return (-1,-1)
显然,上述算法简明,不占额外空间,在数据量小时是不错的解决方案。当数据量大时,因为需要扫描数组两遍,故效率不高。该算法的空间复杂度为常数,时间复杂度为二次。
如果要改善时间复杂度,则需要防止扫描数组两遍。这是想象当我们在扫描数组是遇到一个新元素A,我们需要找到一个以前访问过的元素B,使得这两个元素的和为给定值。由于我们不可能再次访问原数组去获得B,因此我们需要用一种机制迅速提取出B。由于B的值应该为K-A,故可以设立一个哈希集合存储所有访问过的元素。显然,查询B的时间是常数级别的。代码如下:
1 def find_two_sum(values, K): 2 if len(values) >= 2: 3 visited = {} 4 for i in range(len(values)): 5 if values[i] not in visited: 6 if K - values[i] in visited: 7 return (visited[K-values[i]], i) 8 visited[values[i]] = i 9 return (-1, -1)
上述算法的代价是需要O(N)的空间存放哈希集合。另外,哈希集合的实现也会影响算法实际运行的效率。
最后的一个特殊例子。如果数组已经被排序,则无须额外空间,可以线性时间复杂度找到解。代码如下:
1 def find_two_sum(sorted_values, K): 2 if len(sorted_values) >= 2: 3 start, end = 0, len(sorted_values)-1 4 while start < end: 5 if sorted_values[start] + sorted_values[end] == K: 6 return (start, end) 7 elif sorted_values[start] + sorted_values[end] < K: 8 start += 1 9 else: 10 end -= 1 11 return (-1, -1)
在本题中,如果数组无序,则可产生一个数组初始化为(0,1,...,N-1),对应原来输入数组的下标。然后对此数组排序使得其开始元素对应原始数组最小元素的下标。然后,再应用上述算法。但此时时间复杂度为O(NlgN),空间复杂度为O(N),相对基于哈希集合的算法并无优势。
浙公网安备 33010602011771号