tips: unordered_map用来存储数组(val,index)可以快速找到目标值的索引。
1、454四数相加II
给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:
0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
我的思路: 一开始我想的类似动态规划的思路一样,把从下往上一层一层的计算求和和数量,用HashMap存储,最后直接find查找,这样复杂度有点大。
代码随想录: 他把上两行遍历求和统计在map中,下两行也遍历,遍历途中就直接find上两行的map里有没有合适的。而我是全部求出来。1两数之和
也是类似思路。
多层迭代分解成多个子问题 ,这样比全部一起迭代快,全部一起迭代是n^4,但是分解为上下两部分时2*n^2。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 class Solution {public : int fourSumCount (vector<int >& nums1, vector<int >& nums2, vector<int >& nums3, vector<int >& nums4) { std::unordered_map<int ,int > mapi,mapj,mapk,mapl; for (int i = 0 ;i<nums1. size ();i++){ mapl[nums4[i]]++; } for (int i = 0 ;i<nums1. size ();i++){ for (auto &l:mapl){ mapk[nums3[i]+l.first]+=l.second; } } for (int i = 0 ;i<nums1. size ();i++){ for (auto &k:mapk){ mapj[nums2[i]+k.first]+=k.second; } } for (int i = 0 ;i<nums1. size ();i++){ for (auto &ii:mapj){ mapi[nums1[i]+ii.first]+=ii.second; } } return mapi[0 ]; } }; class Solution {public : int fourSumCount (vector<int >& nums1, vector<int >& nums2, vector<int >& nums3, vector<int >& nums4) { std::unordered_map<int ,int > map; for (int i = 0 ;i<nums1. size ();i++) for (int j = 0 ;j<nums2. size ();j++){ map[nums1[i]+nums2[j]]++; } int len = 0 ; for (int i = 0 ;i<nums1. size ();i++) for (int j = 0 ;j<nums2. size ();j++){ auto it = map.find (-nums3[i]-nums4[j]); if (it!=map.end ()){ len+=it->second; } } return len; } };
2、383赎金信
给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。如果可以,返回 true ;否则返回 false 。
magazine 中的每个字符只能在 ransomNote 中使用一次。
我的思路: :这道题之前遇到过类似的242有效的字母异位词
,按照我常规的思路就是直接两个数组都用Hashmap把元素和出现的次数存起来,然后两个map作比较看看他们的数量是否均等。但是在代码随想录-242有效的字母异位词
学到了新思路,就是统计字符串1在map中,然后遍历字符串2,每遍历一次对应的map中的对应字符的数量-1,最后map中全为0则符合题意。
代码随想录: :和我说的方法一样。与之不同的是,他用一个数组来代替HashMap,因为题目说字符都是小写英文,所以是有限的,用数组足够 ,注意看题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 class Solution {public : bool canConstruct (string ransomNote, string magazine) { std::unordered_map<char ,int > ran,mag; int i = 0 ; while (i<ransomNote.size ()){ ran[ransomNote[i]]++; i++; } i=0 ; while (i<magazine.size ()){ if (ran[magazine[i]]!=0 ){ ran[magazine[i]]--; } i++; } for (auto it:ran){ if (it.second!=0 )return false ; } return true ; } }; class Solution {public : bool canConstruct (string ransomNote, string magazine) { int record[26 ] = {0 }; if (ransomNote.size () > magazine.size ()) { return false ; } for (int i = 0 ; i < magazine.length (); i++) { record[magazine[i]-'a' ] ++; } for (int j = 0 ; j < ransomNote.length (); j++) { record[ransomNote[j]-'a' ]--; if (record[ransomNote[j]-'a' ] < 0 ) { return false ; } } return true ; } };
3、15三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
常用Tips: 把数组先排序,再处理。
我的思路: 这道题耗费很长时间没做出来。这道题应该是1两数之和
的加强版,首先都是在一个数组上寻找两个或多个值之和等于target的元素。我的思考是需要保证搜索时多个数的索引不能出现交叉,比如i=10,那就要一只在0~i之间,k就要在0~j之间。不然就会出现重复搜索,加慢速度。
怎么保证搜索过三个元素值的不在重复?
我的思考: 三个数不能出现重复的,比如{1,2,5}和{5,2,1}就重复了。问题是有没有什么机制找出他们的共性??晕了一下我脑子里出现了一些操作,比如 排序之后它两每个元素就都相同 ,不知道是什么时候刷题刷出这个感觉。但是如果用HashMap
的话,这是三个元素没法放在键值里,必须自己重写Hash函数。于是由于我之前看Apollo
的代码在写A*
算法时用到了将下标转为字符串 的做法,我也就萌生了这个想法。
我写的: 只通过了309/313个数据,其他的超时。可能是使用string做Hash键的原因。
代码随想录 : 两层for循环就可以确定 两个数值,可以使用哈希法来确定 第三个数 0-(a+b) 或者 0 - (a + c) 是否在 数组里出现过,其实这个思路是正确的,但是我们有一个非常棘手的问题,就是题目中说的不可以包含重复的三元组。把符合条件的三元组放进vector中,然后再去重,这样是非常费时的,很容易超时,也是这道题目通过率如此之低的根源所在。其他思路比较好的思路是先排序,再双指针搜索 。
思考: 在1两数之和
中用了一层for循环,第二个数用HashMap确定,这道题我一开始想其他的牛b方法,硬是想不到。最后我还是前两个数用for循环,最后一个数用HashMap。但是看了代码随想录之后有了些反思:很多时候解题并不需要什么NB的方法,只是我过分追求于完美导致了最后首尾难顾,时间也过去了。谨记:追求完美,面面俱到,很难成功!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 class Solution {public : vector<vector<int >> threeSum (vector<int >& nums) { unordered_map<int ,vector<int >> maps; std::unordered_set<string> res; vector<vector<int >> re; for (int i = 0 ;i< nums.size ();i++){ for (int j = 0 ;j<i;j++){ auto it = maps.find (-nums[i]-nums[j]); if (it!=maps.end ()){ for (auto &k:it->second){ if (k<j){ vector<int > temp ({nums[i],nums[j],nums[k]}) ; sort (temp.begin (),temp.end ()); auto str = to_string (temp[0 ])+"_" +to_string (temp[1 ])+"_" +to_string (temp[2 ]); if (res.find (str)==res.end ()){ re.emplace_back (vector <int >({nums[i],nums[j],nums[k]})); } res.emplace (str); } } } } maps[nums[i]].emplace_back (i); } return re; } }; class Solution {public : vector<vector<int >> threeSum (vector<int >& nums) { std::sort (nums.begin (),nums.end ()); vector<vector<int >> res; int left = 1 ; int right = 1 ; for (int i = 0 ;i< nums.size ()-2 ;i++){ left = i+1 ; right = nums.size ()-1 ; if (nums[i]>0 )continue ; if (i!=0 && nums[i]==nums[i-1 ])continue ; int target = -nums[i]; while (left < right){ if (left!=i+1 && nums[left]==nums[left-1 ]){ left++; continue ; } if (right!=nums.size ()-1 && nums[right]==nums[right+1 ]){ right--; continue ; } if (nums[left]+nums[right]-target==0 ){ res.emplace_back (vector <int >({nums[i],nums[left],nums[right]})); left++; right--; }else if (nums[left]+nums[right]-target<0 ){ left++; }else if (nums[left]+nums[right]-target>0 ){ right--; } } } return res; } }; class Solution {public : vector<vector<int >> threeSum (vector<int >& nums) { vector<vector<int >> result; sort (nums.begin (), nums.end ()); for (int i = 0 ; i < nums.size (); i++) { if (nums[i] > 0 ) { return result; } 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; } };
4、18四数之和
给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):
我的思路: 这道题三数之和的进阶版。我的思路是前两个数嵌套for循环,后两个数用双指针。
遇到的几个坑,折腾了好久:
for(int i = 0;i< nums.size()-3;i++)
这句代码中num.size()返回的是size_t(long unsigned int)无符号类型,所以-3之后如果是负数会转换为极大的正数。一直循环。。正确的做法for(int i = 0;i< (int)nums.size()-3;i++)
。
if(nums[i]>target)continue;
剪枝只有在target=0
时才有效。替换为nums[i] > target && (nums[i] >=0 || target >= 0)
代码随想录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 class Solution {public : vector<vector<int >> fourSum (vector<int >& nums, int target) { std::sort (nums.begin (),nums.end ()); vector<vector<int >> res; int left = 1 ; int right = 1 ; for (int i = 0 ;i< (int )nums.size ()-3 ;i++){ if (i!=0 && nums[i]==nums[i-1 ])continue ; for (int j = i+1 ;j< (int )nums.size ()-2 ;j++){ if (j!=i+1 && nums[j]==nums[j-1 ])continue ; left = j+1 ; right = nums.size ()-1 ; long target1 = (long )target-nums[i]-nums[j]; while (left < right){ if (left!=j+1 && nums[left]==nums[left-1 ]){ left++; continue ; } if (right!=nums.size ()-1 && nums[right]==nums[right+1 ]){ right--; continue ; } if (nums[left]+nums[right]==target1){ res.emplace_back (vector <int >({nums[i],nums[j],nums[left],nums[right]})); left++; right--; }else if ((long )nums[left]+nums[right]-target1<0 ){ left++; }else if ((long )nums[left]+nums[right]-target1>0 ){ right--; } } } } return res; } };