今天无意中听到同事面试, 问到了add n sum类的问题. 想到leetcode上有很多类似的问题, 特地整理一下。
Q1: 2Sum
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, and you may not use the same element twice.
Example: Given nums = [2, 7, 11, 15], target = 9, Because nums[0] + nums[1] = 2 + 7 = 9, return [0, 1].
算法
- 用一个map保存各个元素和它对应的下标* 每遍历一个元素, 先在map中查找 target - curElement是否存在
- 如果存在, 说明找到了一对满足要求的元素, 直接返回
- 如果不存在, 将<当前元素, 下标>插入到map中时间复杂度是O(n), 空间复杂度是O(n)
实现
class Solution
{
public:
vector<int> twoSum(vector<int>& nums, int target)
{
std::map<int, int> hashMap;
std::vector<int> results;
for (std::vector<int>::iterator it = nums.begin();
it != nums.end();
++it)
{
if (hashMap.find(target - *it) != hashMap.end())
{
results.push_back(hashMap[target - *it]);
results.push_back(it - nums.begin());
break;
}
else
{
hashMap.insert(std::make_pair(*it, it - nums.begin()));
}
}
return results;
}};
Q2: 3Sum
Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0?
Find all unique triplets in the array which gives the sum of zero.Note: The solution set must not contain duplicate triplets.
For example, given array S = [-1, 0, 1, 2, -1, -4], A solution set is:
[ [-1, 0, 1], [-1, -1, 2] ]
需要注意的是, 给定的数组中, 数字是可能重复的.
基本思路和2Sum类似, 先固定一个数字a, 然后在剩下的数字里, 找到2Sum等于 -a 的两个组合.
很显然, 如果我们采用暴力列举所有组合的方法的话, 时间复杂度是O(n^3).
在我们暴力列举的时候, 一定会有很多的重复, 所以下面我们的目标就是把重复的组合过滤掉.
- 为了能够高效地过滤掉重复数字, 我们首先对数组排序.排序后的结果为
[-4, -1, -1, 0, 1, 2]
i----- h ------------p - 固定下标为i的数字, 然后在h ~ p的子集中找到和为 4的组合, 方法是使用两个指针 h , p, 分别指向子数组的开头和结尾, 判断 arr[h] + arr[p]和第二目标数字(4)的大小
- 如果 arr[h] + arr[p] > 0 - arr[i], --p 直到找到第一个和arr[h] 不相等的值
- 如果 arr[h] + arr[p] < 0 - arr[i], ++h 直到找到第一个和arr[h] 不相等的值
- 如果 arr[h] + arr[p] = 0 - arr[i], 说明找到了一组值, 记录下来. 然后同时更新h p直到分别找到第一个不相等的值.这三个地方的去重, 是第一个过滤重复的优化3. 找到所有arr[i]对应的组合后, ++i直到找到第一个不相等的值. 这里是第二个过滤重复的优化.排序的时间复杂度O(nlogn), 查找组合的时间复杂度是O(n^2),
总体时间复杂度是O(n^2). 空间复杂度O(1)
实现
class Solution
{
public:
vector<vector<int>> threeSum(vector<int>& nums)
{
std::vector<std::vector<int> > results;
std::sort(nums.begin(), nums.end());
for (std::vector<int>::iterator it = nums.begin();
it != nums.end();)
{
int target = 0 - *it;
std::vector<int>::iterator head = it + 1;
std::vector<int>::iterator tail = nums.end() - 1;
while (head < tail)
{
std::vector<int> result;
if (*head + *tail == target)
{
--tail;
continue;
}
else if (*head + *tail < target)
{
++head;
continue;
}
else
{
result.push_back(*it);
result.push_back(*head);
result.push_back(*tail);
results.push_back(result);
while (head < tail && *head == result[1]) ++head;
while (tail > head && *tail == result[2]) --tail;
}
}
int curValue = *it;
++it;
while (it < nums.end() && *it == curValue) ++it;
}
return results;
}};
Q3: 4Sum
Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = target?
Find all unique quadruplets in the array which gives the sum of target.
Note: The solution set must not contain duplicate quadruplets.
For example, given array S = [1, 0, -1, 0, -2, 2], and target = 0.
A solution set is: [ [-1, 0, 0, 1], [-2, -1, 1, 2], [-2, 0, 0, 2] ]
算法
基本思路和3Sum类似
- 对数组排序
- 先固定第一个元素, 然后对其后的子数组中, 求 三个元素之和 = target - *p1, 求三元素的方法就是利用3Sum的方法. 这里不再赘述.
时间复杂度 O(n^3), 空间复杂度 O(1).
4Sum的实现如下
class Solution
{
public:
vector<vector<int>> fourSum(vector<int>& nums, int target)
{
std::vector<std::vector<int> > results;
std::vector<int> result;
result.resize(4);
std::sort(nums.begin(), nums.end());
for (std::vector<int>::const_iterator it1 = nums.begin();
it1 != nums.end();)
{
int threeSumTarget = target - *it1;
for (std::vector<int>::const_iterator it2 = it1 + 1;
it2 != nums.end();)
{
int twoSumTarget = threeSumTarget - *it2;
std::vector<int>::const_iterator it3 = it2 + 1;
std::vector<int>::const_iterator it4 = nums.end() - 1;
while (it3 < it4)
{
if (*it3 + *it4 < twoSumTarget)
{
while (++it3 < it4 && *it3 == *(it3 - 1)) continue;
}
else if (*it3 + *it4 > twoSumTarget)
{
while (--it4 > it3 && *it4 == *(it4 + 1)) continue;
}
else
{
result.clear();
result.push_back(*it1);
result.push_back(*it2);
result.push_back(*it3);
result.push_back(*it4);
results.push_back(result);
while (++it3 < it4 && *it3 == *(it3 - 1)) continue;
while (--it4 > it3 && *it4 == *(it4 + 1)) continue;
}
}
while (++it2 != nums.end() && *it2 == *(it2 - 1)) continue;
}
while (++it1 != nums.end() && *it1 == *(it1 - 1)) continue;
}
return results;
}};
小结
Leetcode上还有一些变型的题目, 基础都是上面的三个问题.
从上面的三个问题, 我们可以推导出求xSum的方法:
即逐层固定元素, 将问题简化为求(x-1)Sum, (x-2)Sum ... 2Sum的问题.
时间复杂度为O(n^(x - 1)), 空间复杂度O(1).