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 }

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

  1. 代码随想录算法训练营day07 | leetcode 454.四数相加II 383. 赎金信 15. 三数之和 18. 四数之和

    LeetCode 454.四数相加II 分析1.0 这个最直接暴力法,不过过于暴力了,害怕.jpg 失误 读题理解失误:题目要求的是四元组的个数,读完题到我这里成了输出四元组,悲哉 分析2.0 记录有 ...

  2. 代码随想录算法训练营day01 | leetcode 704/27

    前言   考研结束半个月了,自己也简单休整了一波,估了一下分,应该能进复试,但还是感觉不够托底.不管怎样,要把代码能力和八股捡起来了,正好看到卡哥有这个算法训练营,遂果断参加,为机试和日后求职打下一个 ...

  3. 代码随想录算法训练营day02 | leetcode 977/209/59

    leetcode 977   分析1.0:   要求对平方后的int排序,而给定数组中元素可正可负,一开始有思维误区,觉得最小值一定在0左右徘徊,但数据可能并不包含0:遂继续思考,发现元素分布有三种情 ...

  4. 代码随想录算法训练营day22 | leetcode 235. 二叉搜索树的最近公共祖先 ● 701.二叉搜索树中的插入操作 ● 450.删除二叉搜索树中的节点

    LeetCode 235. 二叉搜索树的最近公共祖先 分析1.0  二叉搜索树根节点元素值大小介于子树之间,所以只要找到第一个介于他俩之间的节点就行 class Solution { public T ...

  5. 代码随想录算法训练营day17 | leetcode ● 110.平衡二叉树 ● 257. 二叉树的所有路径 ● 404.左叶子之和

    LeetCode 110.平衡二叉树 分析1.0 求左子树高度和右子树高度,若高度差>1,则返回false,所以我递归了两遍 class Solution { public boolean is ...

  6. 代码随想录算法训练营day12 | leetcode 239. 滑动窗口最大值 347.前 K 个高频元素

    基础知识 ArrayDeque deque = new ArrayDeque(); /* offerFirst(E e) 在数组前面添加元素,并返回是否添加成功 offerLast(E e) 在数组后 ...

  7. 代码随想录算法训练营day10 | leetcode 232.用栈实现队列 225. 用队列实现栈

    基础知识 使用ArrayDeque 实现栈和队列 stack push pop peek isEmpty() size() queue offer poll peek isEmpty() size() ...

  8. 代码随想录算法训练营day06 | leetcode 242、349 、202、1

    基础知识 哈希 常见的结构(不要忘记数组) 数组 set (集合) map(映射) 注意 哈希冲突 哈希函数 LeetCode 242 分析1.0 HashMap<Character, Inte ...

  9. 代码随想录算法训练营day03 | LeetCode 203/707/206

    基础知识 数据结构初始化 // 链表节点定义 public class ListNode { // 结点的值 int val; // 下一个结点 ListNode next; // 节点的构造函数(无 ...

  10. 代码随想录算法训练营day24 | leetcode 77. 组合

    基础知识 回溯法解决的问题都可以抽象为树形结构,集合的大小就构成了树的宽度,递归的深度构成的树的深度 void backtracking(参数) { if (终止条件) { 存放结果; return; ...

随机推荐

  1. 慢SQL的致胜法宝

    大促备战,最大的隐患项之一就是慢SQL,对于服务平稳运行带来的破坏性最大,也是日常工作中经常带来整个应用抖动的最大隐患,在日常开发中如何避免出现慢SQL,出现了慢SQL应该按照什么思路去解决是我们必须 ...

  2. 微信小程序-应用程序生命周期方法

    官方文档地址:https://developers.weixin.qq.com/miniprogram/dev/reference/api/App.html // app.js App({ onLau ...

  3. 4.8 x64dbg 学会扫描应用堆栈

    堆栈是计算机中的两种重要数据结构 堆(Heap)和栈(Stack)它们在计算机程序中起着关键作用,在内存中堆区(用于动态内存分配)和栈区(用于存储函数调用.局部变量等临时数据),进程在运行时会使用堆栈 ...

  4. Python 开发代码片段笔记

    作者编写的一些代码片段,本版本为残废删减版,没有加入多线程,也没有实现任何有价值的功能,只是一个临时记事本,记录下本人编写代码的一些思路,有价值的完整版就不发出来了,自己组织吧,代码没啥技术含量,毕竟 ...

  5. Rsync+Inotify 实现数据同步

    Rsync 是UNIX及类UNIX-Like平台下一款强大的数据镜像备份软件,它不像FTP或其他文件传输服务那样需要进行全备份,Rsync 可以根据数据的变化进行差异备份,从而减少数据流量,提高工作效 ...

  6. 《Spring 手撸专栏》| 开篇介绍,我要带新人撸 Spring 啦!

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 不正经!写写面经,去撸Spring源码啦? 是的,在写了4篇关于Spring核心源码 ...

  7. CF940F Machine Learning题解

    题目链接:洛谷 或者 CF 不是特别难的题,抽象下题意就是算区间次数出现的次数 mex 和带单点修改.看到范围 \(1e5\) 还带修改,传统的 mex 求法里貌似就莫队类算法好带修,考虑带修莫队. ...

  8. 编译pjsip源码

    操作系统 : Windows 10_x64 [版本 10.0.19042.685] pjsip版本 : 2.10 pjsip官网:https://www.pjsip.org/ 1. 下载pjsip源代 ...

  9. HASHTEAM香山杯2023WP

    目录 前言 misc 签到题 web PHP_unserialize_pro Re URL从哪儿来 hello python pwn Move pwthon 附上c-python调试方法 crypto ...

  10. STM32 printf 方法重定向到串口UART

    在嵌入式系统中调试代码是很麻烦的一件事, 如果能方便地输出调试信息(与调试者交互), 能使极大加快问题排查的过程. 串口在嵌入式领域是一个比较重要的通讯接口. 因为没有显示设备, 在单片机的程序里调用 ...