题目:
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
UPDATE (2016/2/13):
The return format had been changed to zero-based indices. Please read the above updated description carefully.
思路:
1、暴力枚举法:两重循环搜索所有的情况,时间复杂度O(n^2),空间复杂度O(1)。肯定无法满足题目要求。
2、将数组排序(前提是允许修改原数组),然后用两个指针从左右分别扫描数组:
1)如果当前和大于target,则右指针左移一位;
2)如果当前和小于target,则左指针右移一位;
3)如果当前和等于target,则加入结果集中,并返回(由于题目中假设每对输入中有且仅有一对结果)。
这种方法中排序的时间复杂度是O(n*logn),扫描数组的时间复杂度是O(n),所以总体时间复杂度是O(n*logn),空间复杂度是O(1)。
3、定义一个hash表,并且依次扫描原数组中的数。如果hash表中存在(target - nums[i]),则分别两者的下标;否则将nums[i]放入hash表中。这种方法的时间复杂度可以降至O(n),但是空间复杂度为O(n),是一种典型的用空间换时间的做法。
注意target - nums[i]有可能越界,所以需要将remain的类型定义为long long(不过目前的测试用例似乎没有测试到这种edge case)。
代码:
C++:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target)
{
unordered_map<int, int> hashMap;
std::vector<int> indices;
for (size_t i = 0; i < nums.size(); i++)
{
long long remain = target - nums[i]; // avoid the overflow of int
if(remain < INT_MIN || remain > INT_MAX)
continue;
if (hashMap.find(remain) != hashMap.end())
{
indices.push_back(hashMap[remain]);
indices.push_back(i);
return indices;
}
hashMap.insert(pair<int, int>(nums[i], i));
}
return indices;
}
};
Python3:
class Solution:
def twoSum(self, nums: 'List[int]', target: 'int') -> 'List[int]':
if len(nums) <= 1:
return False
indices = {}
for i in range(len(nums)):
if nums[i] in indices:
return [indices[nums[i]], i] # return the results
else:
indices[target - nums[i]] = i # add this to indices
Follow up:
1、如果算法可能无解,或者有多个解,则现有算法需要如何修改?
考虑到target具有不同的分解方式,例如3+5=8,并且2+6=8,所以在获得一个解时就不能提前返回了。此外,由于数组中还可能有重复元素,所以最好将hashMap的value类型修改为set<int>,而且必须首先对整个数组做一个遍历,建立起一个包含完整数据的hashMap,然后再对数组进行第二次扫描。在这个过程中,为进一步提高效率,可以考虑第一次扫描时,在hashMap中只存放小于等于target/2的值。然后在第二次扫描时,只有遇到大于target/2的值,才在hashMap中找对应项。当然最后别忘了处理一下target/2的特殊情况。
2、如果给定的数组特别大,则怎么办?如果有多台计算机可供使用,则如何优化算法?
如果数组特别大,则意味着无法将它们全部同时加入内存。假如有多台计算机,可以考虑将数组拆分,然后分别计算,最后合成结果。假如有n台计算机,我们可以将数组中小于等于target/2的所有数分成n份,然后分别计算。假如其中一份的范围是[left_min, left_max],则在该计算机上只考虑范围在[target - left_max, target - left_min]之内的数。最后将每台计算机上的计算结果合并起来即可。此时target/2的情况也需要特殊处理。至于如何拆分数据,则有两种思路可供考虑:1)在一台超级计算机上将数据将数据分成n份,然后分别发送到各台计算机;2)将数组数据复制n份,分别存到各台计算机上,然后每台计算机处理的时候,只处理处于自己处理范围之内的数据。这两种方式各有优劣。