首页 > 代码库 > [LeetCode] 4Sum

[LeetCode] 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:

  • Elements in a quadruplet (a,b,c,d) must be in non-descending order. (ie, a ≤ b ≤ c ≤ d)
  • 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,先排序,这后两重循环,然后对最后的一个数组设两个指针遍历。这里注意重复的问题,复杂度 排序O(log n)+遍历O(n^3)

code如下

 1 class Solution { 2 private: 3     vector<vector<int> > ret; 4 public: 5     vector<vector<int> > fourSum(vector<int> &num, int target) { 6         // Start typing your C/C++ solution below 7         // DO NOT write int main() function 8         sort(num.begin(), num.end()); 9         10         ret.clear();11         12         for(int i = 0; i < num.size(); i++)13         {14         //skip dupliated15             if (i > 0 && num[i] == num[i-1])16                 continue;17                 18             for(int j = i + 1; j < num.size(); j++)19             {20             //skip dupliated21                 if (j > i + 1 && num[j] == num[j-1])22                     continue;23                     24                 int k = j + 1;25                 int m = num.size() - 1;26                 27                 while(k < m)28                 {29                     if (k > j + 1 && num[k] == num[k-1])30                     {31                 //skip dupliated32                         k++;33                         continue;34                     }35                     36                     if (m < num.size() - 1 && num[m] == num[m+1])37                     {38                 //skip dupliated39                         m--;40                         continue;41                     }42                     43                     int sum = num[i] + num[j] + num[k] + num[m];44                     45                     if (sum == target)46                     {47                         vector<int> tmp;48                         tmp.push_back(num[i]);49                         tmp.push_back(num[j]);50                         tmp.push_back(num[k]);51                         tmp.push_back(num[m]);52                         ret.push_back(tmp);53                         k++;54                     }55                     else if (sum < target)56                         k++;57                     else58                         m--;                        59                 }60             }61         }62         63         return ret;64     }65 };

 

方法二:那么时间复杂度能不能更好呢?其实我们可以考虑用二分法的思路,如果把所有的两两pair都求出来,然后再进行一次Two Sum的匹配,我们知道Two Sum是一个排序加上一个线性的操作,并且把所有pair的数量是O((n-1)+(n-2)+...+1)=O(n(n-1)/2)=O(n^2)。所以对O(n^2)的排序如果不用特殊线性排序算法是O(n^2*log(n^2))=O(n^2*2logn)=O(n^2*logn),算法复杂度比上一个方法的O(n^3)是有提高的。
思路虽然明确,不过细节上会多很多情况要处理。首先,我们要对每一个pair建一个数据结构来存储元素的值和对应的index,这样做是为了后面当找到合适的两对pair相加能得到target值时看看他们是否有重叠的index,如果有说明它们不是合法的一个结果,因为不是四个不同的元素。接下来我们还得对这些pair进行排序,所以要给pair定义comparable的函数。最后,当进行Two Sum的匹配时因为pair不再是一个值,所以不能像Two Sum中那样直接跳过相同的,每一组都得进行查看,这样就会出现重复的情况,所以我们还得给每一个四个元素组成的tuple定义hashcode和相等函数,以便可以把当前求得的结果放在一个HashSet里面,这样得到新结果如果是重复的就不加入结果集了。

 

如果使用的是基于红黑树的map,查找效率O(logn),基于hash的map,查找复杂度是O(n),unordered_multimap是基于C++11的hash map

 第二种方法比第一种方法时间上还是有提高的,其实这道题可以推广到k-Sum的问题,基本思想就是和第二种方法一样进行二分,然后两两结合,不过细节就非常复杂了(这点从上面的第二种解法就能看出来),所以不是很适合在面试中出现,有兴趣的朋友可以进一步思考或者搜索网上材料哈

code如下

 

 1 class Solution { 2     public: 3         vector<vector<int>> fourSum(vector<int>& num, int target) { 4             vector<vector<int>> result; 5             if (num.size() < 4) return result; 6             sort(num.begin(), num.end()); 7             unordered_multimap<int, pair<int, int>> cache;// 基于哈希,可以存储多个相同的key 值 8             for (int i = 0; i + 1 < num.size(); ++i) //保存所有的两两配对 9                 for (int j = i + 1; j < num.size(); ++j)10                     cache.insert(make_pair(num[i] + num[j], make_pair(i, j)));11             for (unordered_multimap<int, pair<int, int>>::iterator i = cache.begin(); i != cache.end(); ++i) {12                 int x = target - i->first;//计算另外的数13                 pair<unordered_multimap<int, pair<int, int>>::iterator,unordered_multimap<int, pair<int, int>>::iterator> range;14                 //auto range15                 range = cache.equal_range(x);//在cache中查找16                 //返回的i,j就是指向满足条件的首位迭代器17                 for (unordered_multimap<int, pair<int, int>>::iterator j = range.first; j != range.second; ++j) {18                     int idx1 = i->second.first;19                     int idx2 = i->second.second;20                     int idx3 = j->second.first;21                     int idx4 = j->second.second;22                     if (idx1 != idx3 && idx1 != idx4 && idx2 != idx3 && idx2 != idx4) {23                         vector<int> vec = { num[idx1], num[idx2], num[idx3], num[idx4] };24                         sort(vec.begin(), vec.end());25                         result.push_back(vec);26                     }27                 }28             }29             sort(result.begin(), result.end());30             result.erase(unique(result.begin(), result.end()), result.end());31             /*32              *  unique(result.begin(), result.end()) 将result unique化,返会的是不重复的最后一个元素下一个的迭代器,33              *  erase将后续重复的内容擦除34              */35             return result;36         }37 };