几数之和的题目
总结:
两数之和——哈希表:时间复杂度O(n),暴力解法O(n^2)
三数之和——双指针:一层i的for循环,双指针left和right。时间复杂度O(n^2),暴力解法O(n^3)
四数之后——双指针:两层i和k的for循环,双指针left和right,时间复杂度O(n^3),暴力解法O(n^4)
再增加也是一样的,三个、四个甚至更多数之和都是这个方法,代码也是差不多,思路都一样的,有几个小地方需要注意:(1)给的数组一定要进行排序;(2)每个下标的数字都要进行去重,循环里头的去重和双指针的left和right去重,去重基本都是当前位和前一位去比较,不能当前位和下一位比较,别的倒也是没啥了,细心一点就能写对。
四数相加II那道题不太一样,要求不一样,这个更为简单一些。
class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { unordered_map <int,int> map;//不需要有序,所以用这个 for(int i = 0; i < nums.size(); i++) { auto iter = map.find(target - nums[i]);//find函数返回的是迭代器 if(iter != map.end()) {//这句话表示找到了target-nums[i]这个元素 return {iter->second, i};//返回找到的元素的下标,还有i这个下标 } map.insert(nums[i], i);//如果没找到,不存在里头的话就添加进行 } return {}; } }; //这道题还有变体,要是说给你一个数组,让你在里头找所有两个数之和等于target的元组,那就跟三数之和四数之后一样了,双指针法解决,注意去重。
以上这个写法理论上没错,但是力扣报错,不知道为什么,下面的写法更容易理解
class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { unordered_map<int, int> map; // 先将元素放到哈希表里面去 for (int i = 0; i < nums.size(); i++) { map.insert(make_pair(nums[i], i)); } // 下面开始判断是否存在 for (int i = 0; i < nums.size(); i++) { int another = target - nums[i]; auto iter = map.find(another); if (iter != map.end() && iter->second != i) { return {i, iter->second}; } } return {}; } };
class Solution { public: vector<vector<int>> threeSum(vector<int>& nums) { //这道题如果使用哈希表解决的话,去重会非常的麻烦,所以最好是采用双指针解法 //先对数组排序,判断特殊情况,过程中要注意去重操作,特别是去重的位置,时机不对都会漏掉情况 vector<vector<int>> result;//定义二维数组容器 sort(nums.begin(), nums.end()); //找出a+b+c = 0 //a=nums[i], b=nums[left], c=nums[right] for(int i=0; i<nums.size(); i++) { //特殊情况,排序之后如果第一个元素都大于0了,那就不能存在了 if(nums[i] > 0) return result; //去重,这里一定要注意,不能是i和i+1进行比较去重,否则会漏掉-1,-1,2这种情况。 if(i>0 && nums[i]==nums[i-1]) continue; int left = i+1; int right = nums.size()-1; while(right > left) { if(nums[i]+nums[left]+nums[right] > 0) right--; else if(nums[i]+nums[left]+nums[right] < 0) left++; else { result.push_back(vector<int>{nums[i], nums[left], nums[right]});//将这个结果放到二维数组中 //去重逻辑 while(right>left && nums[right]==nums[right-1]) right--; while(right>left && nums[left]==nums[left+1]) left++; //找到答案,双指针同时收缩,因为是排序的数组 right--; left++; } } } return result; } };
class Solution { public: vector<vector<int>> fourSum(vector<int>& nums, int target) { vector<vector<int>> result; sort(nums.begin(), nums.end()); for(int k=0; k<nums.size(); k++) { //肯定要对k这这个最前面的元素进行去重,但不能是i和i+1,否则会漏掉情况 if(k>0 && nums[k]==nums[k-1]) continue; for(int i=k+1; i<nums.size(); i++) { //对i也要进行去重,同上 if(i>k+1 && nums[i]==nums[i-1]) continue; int left = i+1; int right = nums.size()-1; while(right>left) { if(nums[k]+nums[i]+nums[left]+nums[right] > target) right--; else if(nums[k]+nums[i]+nums[left]+nums[right] < target) left++; else { result.push_back(vector<int>{nums[k],nums[i],nums[left],nums[right]}); //去重逻辑 while(right>left && nums[right]==nums[right-1]) right--; while(right>left && nums[left]==nums[left+1]) left++; //找到了一组值,left和right同步缩进 right--; left++; } } } } return result; } };
class Solution { public: int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) { //本题的思路可以是: //利用哈希表,先统计a+b的值,把值作为map的key,出现的次数作为value, //然后遍历c和d,如果在map中找到了0-(c+d)的值,就把个数+1,直到最后 unordered_map<int, int> umap; for(int a : A) { for(int b : B) { umap[a+b]++; } } int count = 0; for(int c : C) { for(int d : D) { if(umap.find(0-(c+d)) != umap.end()) count += umap[0-(c+d)]; } } return count; } };