代码随想录算法训练营第七天| LeetCode 454.四数相加II 15. 三数之和 18. 四数之和

发布时间 2023-08-02 02:04:10作者: 银河小船儿

454.四数相加II

      卡哥建议:本题是使用map巧妙解决的问题,好好体会一下 哈希法如何提高程序执行效率,降低时间复杂度,当然使用哈希法会提高空间复杂度,但一般来说我们都是舍空间换时间, 工业开发也是这样。

      题目链接/文章讲解/视频讲解:https://programmercarl.com/0454.%E5%9B%9B%E6%95%B0%E7%9B%B8%E5%8A%A0II.html

      做题思路:

        暴力解法用4个for循环,会超时。为啥用map做,是因为我们可以把遍历4个数组abcd,变成遍历两个数组(a+b),(c+d)。第一个数组先计算a+b的和,把和存到容器中,在遍历第二个数组c+d的时候,再到容器中判断有没有我们想要的元素(0-(a+b))。因为这个题最后是输出多少个符合要求,所以除了看有没有出现我们想要的元素外,还要统计出现的次数,元素和次数对应,在map中是key对应着value。需要注意的是次数不是加1,要加value里记录的数,视频里卡哥讲了这点。

      代码的输入和第6天的题代码输入差不多,我这里不写了。

 1 int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
 2         unordered_map<int, int> umap; //key:a+b的数值,value:a+b数值出现的次数
 3         // 遍历大A和大B数组,统计两个数组元素之和,和出现的次数,放到map中
 4         for (int a : A) {
 5             for (int b : B) {
 6                 umap[a + b]++; //映射到了次数,然后++ 
 7             }
 8         }
 9         int count = 0; // 统计a+b+c+d = 0 出现的次数
10         // 在遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就把map中key对应的value也就是出现次数统计出来。
11         for (int c : C) {
12             for (int d : D) {
13                 if (umap.find(0 - (c + d)) != umap.end()) {
14                     count += umap[0 - (c + d)]; //次数要加value 
15                 }
16             }
17         }
18         return count;
19     }

15. 三数之和

        卡哥建议:本题虽然和 两数之和 很像,也能用哈希法,但用哈希法会很麻烦,双指针法才是正解,可以先看视频理解一下 双指针法的思路,文章中讲解的,没问题 哈希法很麻烦。 

      题目链接/文章讲解/视频讲解:https://programmercarl.com/0015.%E4%B8%89%E6%95%B0%E4%B9%8B%E5%92%8C.html

      做题思路:

      这个题用哈希法就和上个题的思路差不多,就是去重不好处理。这道题目使用双指针法 要比哈希法高效一些。先排完序后,用for循环中的 i 代表 a,双指针的 left 代表 b,right 代表 c,这里卡哥视频讲解更清晰!其中去重要判断 num[i] = num[i+1],num[i] = num[i-1],用的是num[i] = num[i-1],比如(-1,-1,2),当我们从第一个 -1 看,那(-1,-1,2) 要算一个结果,从第二个 -1 开始向前看有没有-1,如果有的话,那直接 continue,从下一个位置 i 开始;对 left,right 也得像num[i] 那样判断,不同的是,在一个for 循环确定 i 下,双指针靠移动,就是如果重复了,那双指针移动去检查下一个是否重复。

        代码:

 1 vector<vector<int>> threeSum(vector<int>& nums) {
 2         vector<vector<int>> result; //结果集 
 3         sort(nums.begin(), nums.end()); //先排序 
 4         // 找出a + b + c = 0
 5         // a = nums[i], b = nums[left], c = nums[right]
 6         for (int i = 0; i < nums.size(); i++) {
 7             // 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了
 8             if (nums[i] > 0) {
 9                 return result;
10             }
11             // 错误去重a方法,将会漏掉-1,-1,2 这种情况
12             /*
13             if (nums[i] == nums[i + 1]) {
14                 continue;
15             }
16             */
17             // 正确去重a方法
18             if (i > 0 && nums[i] == nums[i - 1]) {
19                 continue;
20             }
21             int left = i + 1; //双指针的初始化 
22             int right = nums.size() - 1;
23             while (right > left) { //b 不能等于 c 
24                 // 去重复逻辑如果放在这里,0,0,0 的情况,可能直接导致 right<=left 了,从而漏掉了 0,0,0 这种三元组
25                 /*
26                 while (right > left && nums[right] == nums[right - 1]) right--;
27                 while (right > left && nums[left] == nums[left + 1]) left++;
28                 */
29                 if (nums[i] + nums[left] + nums[right] > 0) right--;  //,缩小范围,right向前移动 
30                 else if (nums[i] + nums[left] + nums[right] < 0) left++; //扩大范围,left向后移动 
31                 else { //等于0的情况 
32                     result.push_back(vector<int>{nums[i], nums[left], nums[right]}); //把3个数放进结果集,3个数也在容器里,所以result一开始时定义的时候容器里有容器 
33                     //去重逻辑应该放在找到一个三元组之后,对b 和 c去重
34                     while (right > left && nums[right] == nums[right - 1]) right--;
35                     while (right > left && nums[left] == nums[left + 1]) left++;
36 
37                     // 找到答案时,双指针同时收缩
38                     right--;  //放进后,双指针继续同时移动找下一个区间,重新进入下一个while循环 
39                     left++;
40                 }
41             }
42 
43         }
44         return result;
45     }

18. 四数之和 

     卡哥建议: 要比较一下,本题和 454.四数相加II 的区别,为什么 454.四数相加II 会简单很多,这个想明白了,对本题理解就深刻了。 本题 思路整体和 三数之和一样的,都是双指针,但写的时候 有很多小细节,需要注意,建议先看视频。 

     题目链接/文章讲解/视频讲解:https://programmercarl.com/0018.%E5%9B%9B%E6%95%B0%E4%B9%8B%E5%92%8C.html

       做题思路:

     这题要去重,还是用双指针法。用两个for循环确定k , i,剩下的和上一题的思路差不多。这里不能直接剪枝 num[k] > target,因为target 可能是负数,只有 target 和 num[k] 是正数,>0 的时候,才能直接剪枝。在num[k]+num[i] = target 的基础上去重后,再进行双指针的移动。

     代码:

 1 vector<vector<int>> fourSum(vector<int>& nums, int target) {
 2         vector<vector<int>> result;
 3         sort(nums.begin(), nums.end());
 4         for (int k = 0; k < nums.size(); k++) {
 5             // 剪枝处理
 6             if (nums[k] > target && nums[k] >= 0) {
 7                 break; // 这里使用break,统一通过最后的return返回
 8             }
 9             // 对nums[k]去重
10             if (k > 0 && nums[k] == nums[k - 1]) {
11                 continue;
12             }
13             for (int i = k + 1; i < nums.size(); i++) {
14                 // 2级剪枝处理
15                 if (nums[k] + nums[i] > target && nums[k] + nums[i] >= 0) {
16                     break;
17                 }
18 
19                 // 对nums[i]去重
20                 if (i > k + 1 && nums[i] == nums[i - 1]) {
21                     continue;
22                 }
23                 int left = i + 1;
24                 int right = nums.size() - 1;
25                 while (right > left) {
26                     // nums[k] + nums[i] + nums[left] + nums[right] > target 会溢出
27                     if ((long) nums[k] + nums[i] + nums[left] + nums[right] > target) {
28                         right--;
29                     // nums[k] + nums[i] + nums[left] + nums[right] < target 会溢出
30                     } else if ((long) nums[k] + nums[i] + nums[left] + nums[right]  < target) {
31                         left++;
32                     } else {
33                         result.push_back(vector<int>{nums[k], nums[i], nums[left], nums[right]});
34                         // 对nums[left]和nums[right]去重
35                         while (right > left && nums[right] == nums[right - 1]) right--;
36                         while (right > left && nums[left] == nums[left + 1]) left++;
37 
38                         // 找到答案时,双指针同时收缩
39                         right--;
40                         left++;
41                     }
42                 }
43 
44             }
45         }
46         return result;
47     }