day07 四数相加Ⅱ 赎金信 三数之和 四数之和

题目1:454  四数相加Ⅱ

题目链接:454 四数相加Ⅱ

题意

4个整数数组nums1, nums2, nums3, nums4的长度均为n,有多少个元组(i,j,k,l)使得

nums[i]+nums[j]+nums[k]+nums[l]==0   (本题不包含去重的逻辑,i,j,k,l  可以相等,一组元素与一组元素之间的各个元素也可以相等)

暴力解法

class Solution {
public:int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {int count = 0;for(int i=0;i<nums1.size();i++){for(int j=0;j<nums2.size();j++){for(int k=0;k<nums3.size();k++){for(int l=0;l<nums4.size();l++){if(nums1[i]+nums2[j]+nums3[k]+nums4[l]==0){count++;}}}}}return  count;}
};

会报如下超时错误

map

数组的数值较大且较为分散,所以数组可以排除

4个元素分组,分成两组(这样的时间复杂度最低O(n^2))nums1与nums2  nums3与nums4

其中一组的元素和是否出现过,还要知道出现过的次数(value),因此使用map

unordered_map的读写效率最高

步骤

① 遍历nums1和nums2,求解nums1[i]+nums2[j]的和,将这个和及其出现的次数放到map中

② 遍历nums3和nums4,在map中查询0-(nums3[k]+nums4[l])

如果存在key==0-(nums3[k]+nums4[l])  就将count加上key对应的value,这点很重要,一定要加value,因为这个key可能在nums1和nums中出现多种组合,这都是满足题目要求的!!!

例如:

nums1[1]+nums1[2]=5

nums1[1]+nums1[4]=5

nums1[3]+nums2[5]=5

nums3[1]+nums4[1]=-5

这种就是nums1[i]+nums2[j]在map中存在多种组合,key==5时,对应的value==3

因此,count=count+3

单纯下标

代码

class Solution {
public:int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {unordered_map<int,int> map;//定义map,存放nums1[i]+nums2[j]int count = 0;for(int i=0;i<nums1.size();i++){for(int j=0;j<nums2.size();j++){map[nums1[i]+nums2[j]]++;}}for(int k=0;k<nums3.size();k++){for(int l=0;l<nums4.size();l++){int target = 0 - (nums3[k]+nums4[l]);if(map.find(target)!=map.end()){count += map[target]; //加上target(即nums[i]+nums[j])出现的次数}}}return count;}
};
  • 时间复杂度: O(n^2)
  • 空间复杂度: O(n^2),最坏情况下A和B的值各不相同,相加产生的数字个数为 n^2
简便

代码

class Solution {
public:int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {unordered_map<int,int> map;int count = 0;for(int a:nums1){for(int b:nums2){map[a+b]++;}}for(int c:nums3){for(int d:nums4){if(map.find(0-(c+d))!=map.end()){count += map[0-(c+d)];}}}return count;}
};
  • 时间复杂度: O(n^2)
  • 空间复杂度: O(n^2),最坏情况下A和B的值各不相同,相加产生的数字个数为 n^2

以上解法和有效字母异位词比较相像,都是先遍历数组,对哈希表进行插入操作;再遍历另外的数组,在哈希表中进行查询操作

题目2:赎金信

题目链接:383 赎金信

题意

判断ransomNote字符串能否由magazine字符串中的字符组成,二者均由小写英文字母构成,但是magzine中的每个字符只能使用1次,这两点很关键

即判断ransomNote字符串中的字符是否在magazine中出现过,因此使用哈希表

数组

由于两个字符串中的元素只包含小写字母,是连续的,是有限值且有有限个,因此想到使用数组

步骤

一定要先遍历magazine字符串(因为是在magazine字符串中查找ransomNote中的字符是否存在),统计magazine字符串中每个字符出现的次数(每个字符只能使用1次),记录在hash数组中;

再遍历ransomNote字符串,在上述统计的次数的基础上,遇到相同的字符,在hash数组中对应元素做减减的操作;

最后遍历hash数组,看是否有小于0的元素,若出现小于0的元素,证明ransoNote字符串中的对应该字符数量多于magazine字符串中该字符的数量,说明ransomNote字符串不能由magazine字符串中的字符组成

代码

class Solution {
public:bool canConstruct(string ransomNote, string magazine) {int hash[26] = {0};for(int i=0;i<magazine.size();i++){hash[magazine[i]-'a']++;}for(int i=0;i<ransomNote.size();i++){hash[ransomNote[i]-'a']--;}for(int i=0;i<26;i++){if(hash[i]<0){return false;}}return true;}
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(1),hash数组的大小是常数

代码也可以这样写,hash减减后,直接判断hash中该元素是否小于0,若小于0的话,直接return false即可,就不用后面的hash元素再继续减减操作了

class Solution {
public:bool canConstruct(string ransomNote, string magazine) {int hash[26] = {0};for(int i=0;i<magazine.size();i++){hash[magazine[i]-'a']++;}for(int i=0;i<ransomNote.size();i++){hash[ransomNote[i]-'a']--;if(hash[ransomNote[i]-'a']<0){return false;}}return true;}
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(1),hash数组的大小是常数

暴力解法(不推荐)

如果遇到magazine[i]==ransomNote[i],就将ransomNote中的该字符删掉,同时一定要使用break跳出这个内部for循环,因为magazine中的一个字符只能匹配ransomNote中的1个字符,如果匹配上了,那么这个字符就废了,不能再重复使用了,需要继续下一个magazine元素的循环

代码

!!!注意break的使用

class Solution {
public:bool canConstruct(string ransomNote, string magazine) {for(int i=0;i<magazine.size();i++){for(int j=0;j<ransomNote.size();j++){if(magazine[i]==ransomNote[j]){ransomNote.erase(ransomNote.begin()+j);break;//这里一定要break,因为magazine[i]已经和ransomNote[j]匹配了,再往下让magazine[i]匹配元素不满足要求       //(magazine中的字符只能在ransomNote中使用1次),所以使用break跳出这层内部for循环,继续i++下一个magazine的元素}}}if(ransomNote.size()==0){return true;}else{return false;}}
};
  • 时间复杂度: O(n^2)
  • 空间复杂度: O(1)

题目3:15 三数之和

题目链接:15 三数之和

题意

1个整数数组nums ,找出nums[i]+nums[j]+nums[k]==0的[i,j,k]的组合,其中i,j,k互不相等(意味着元素不能重复使用一组中的nums[i],nums[j],nums[k]的组合不能和另一组中的nums[i],nums[j],nums[k]完全相同,比如:0 1 -1 和 1 0 -1这就重复了,所以需要去除一组

去重是关键

哈希法(不推荐)

使用哈希表去重细节较多

双指针法

不可以先将数组中相同的元素删除,因为可能会使得一些满足条件的组合消失,例如:2 2 -4 

开始时一定要对数组进行排序 

逻辑反例
例1,nums[left],nums[right]去重放置的位置

是和nums[i]一样,放在前面,还是放在while循环的最后面

例2,是先移动left,right还是先去重

这里在写代码时,产生了矛盾,当元素组合满足条件时,到底是先移动还是先去重呢?

丢掉解

重复解

例3,if(nums[i]==nums[i-1]){},{}里是i++,还是continue

想的比较单纯:遇到重复值,我跳过去,不就OK了嘛,但是事实不是这样,有可能有连续的多个重复的值,所以使用i++就产生了错误

例4,如果跳过了重复值,会不会错过一些遍历重复值过程中的组合呢

例5  nums[left] nums[right]去重逻辑里面while循环加入right>left

如果不加right<left的限制会报如下错误

代码

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++){//nums[i]剪枝 一级剪枝if(nums[i]>0){return result;//这里是return result 不能return {},在此之前可能已经有结果了//break;//这里使用break,也是一样的,直接return结果,结束这个for循环}//nums[i]去重  一级去重if(i>0 && nums[i]==nums[i-1]){continue;//继续下一次循环//注意这里continue不能写i++,因为要是写i++的话,对于连续3个相同的数字,会算两遍,看反例3}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]});//去重去的是满足条件的相同的组合,因此,要放在else里面while(right>left && nums[right]==nums[right-1]){right--;}while(right>left && nums[left]==nums[left+1]){left++;}left++;//一定要在去重之后再移动left和right两个指针right--;} }}return result;}
};
  • 时间复杂度: O(n^2)
  • 空间复杂度: O(1)

题目4:18 四数之和

题目链接:18 四数之和

题意

整数数组nums的长度为n,返回nums[a]+nums[b]+nums[c]+nums[d]==target的元组[nums[a],nums[b],nums[c],nums[d]],其中a,b,c,d互不相等(意味着元素不能重复使用),返回的元组不能重复,但元组内部的元素可以重复(因为数组nums中可能存在重复的元素)

双指针法

和上题三数之和一样,同样使用双指针法解决

剪枝的时候要注意,因为target可正可负可零,所以剪枝逻辑就不能像三数之和那样,直接判断nums[i]>target就行了,若target可能为负数,若nums[i]>target,若nums[i+1]还是负数,那么nums[i]+nums[i+1]<nums[i],可能会出现四数之和等于target的情况,如下例:

1) 对于一级剪枝,所以一级剪枝要确定target>0&&nums[i]>target

同样地,对于二级剪枝,也要注意这些细节,二级剪枝时,将nums[i]+nums[j]视作一个整体,与target进行判断

流程

代码

class Solution {
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {vector<vector<int>> result;sort(nums.begin(),nums.end());for(int i=0;i<nums.size();i++){//一级剪枝if(nums[i]>target && target>=0){//if(nums[i]>target && (target>=0 || nums[i]>=0))return result;}//一级去重if(i>0 && nums[i]==nums[i-1]){continue;}for(int j=i+1;j<nums.size();j++){//二级剪枝 将nums[i]+nums[j]看作一个整体if(nums[i]+nums[j]>target && target>=0){//if(nums[i]+nums[j]>target && (target>=0 || nums[i]+nums[j]>=0))break;}//二级去重if(j>i+1 && nums[j]==nums[j-1]){continue;}int left = j + 1;int right = nums.size() - 1;while(right>left){if((long) nums[i]+nums[j]+nums[left]+nums[right]>target){right--;}else if((long) nums[i]+nums[j]+nums[left]+nums[right]<target){left++;}else{result.push_back(vector<int>{nums[i],nums[j],nums[left],nums[right]});//nums[left] nums[right]去重while(right>left && nums[left]==nums[left+1]){left++;}while(right>left && nums[right]==nums[right-1]){right--;}left++;right--;}}}}return result;}
};
  • 时间复杂度: O(n^3)
  • 空间复杂度: O(1)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/602276.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python如何生成个性二维码

Python-生成个性二维码 一、问题描述 通过调用MyQR模块来实现生成个人所需二维码。 安装&#xff1a; pip install myqr 二、代码实现 1.普通二维码 from MyQR import myqr # 普通二维码 myqr.run(wordshttp://www.csdn.net/mayi0312,save_nameqrcode.png ) 效果图&#…

@JsonFormat与@DateTimeFormat

JsonFormat注解很好的解决了后端传给前端的格式&#xff0c;我们通过使用 JsonFormat可以很好的解决&#xff1a;后台到前台时间格式保持一致的问题 其次&#xff0c;另一个问题是&#xff0c;我们在使用WEB服务的时&#xff0c;可 能会需要用到&#xff0c;传入时间给后台&am…

buuctf-Misc 题目解答分解109-111

109.[CFI-CTF 2018]webLogon capture 流量包分析&#xff0c; wireshark 打开 就这几个数据包&#xff0c;追踪http 进行url 解码 URL网址解码器 - 在线网址解码 得到flag CFI{1ns3cur3_l0g0n} 110.[GKCTF 2021]excel 骚操作 下载 excel 文件 &#xff0c;打开 发现点击其他地…

计算机Java项目|基于Springboot实现患者管理系统

作者主页&#xff1a;编程指南针 作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、掘金特邀作者、多年架构师设计经验、腾讯课堂常驻讲师 主要内容&#xff1a;Java项目、毕业设计、简历模板、学习资料、面试题库、技术互助 文末获取源码 项目编号&#xff1a;KS-032…

CanFestival结合Android来完成canopen通信

1.准备开发环境 安装Android Studio和NDK后&#xff0c;需要在Android Studio中创建一个新的NDK项目&#xff0c;并且在项目目录下创建一个jni目录来放置NDK代码。 配置CAN总线接口硬件需要根据具体的硬件要求进行&#xff0c;常见的方法包括使用串口或USB连接CAN总线接口&…

PyTorch Tutorial

本文作为博客“Transformer - Attention is all you need 论文阅读”的补充内容&#xff0c;阅读的内容来自于 https://pytorch.org/tutorials/intermediate/char_rnn_classification_tutorial.html#recommended-preparation 建议的准备流程。 Deep Learning with PyTorch: …

网络知识-以太网技术的发展及网络设备

目 录 一、背景介绍 &#xff08;一&#xff09;网络技术的时代 &#xff08;二&#xff09;以太网技术脱颖而出 二、以太网的工作原理 &#xff08;一&#xff09;、载波侦听多路访问&#xff08;CSMA/CD&#xff09; 1、数据发送流程 2、发送过程解析 3、…

WeNet语音识别+Qwen-72B-Chat Bot+Sambert-Hifigan语音合成

WeNet语音识别Qwen-72B-Chat Bot&#x1f47e;Sambert-Hifigan语音合成 简介 利用 WeNet 进行语音识别&#xff0c;使用户能够通过语音输入与系统进行交互。接着&#xff0c;Qwen-72B-Chat Bot作为聊天机器人接收用户的语音输入或文本输入&#xff0c;提供响应并与用户进行对话…

RPC基础知识总结

RPC 是什么? RPC&#xff08;Remote Procedure Call&#xff09; 即远程过程调用&#xff0c;通过名字我们就能看出 RPC 关注的是远程调用而非本地调用。 为什么要 RPC &#xff1f; 因为&#xff0c;两个不同的服务器上的服务提供的方法不在一个内存空间&#xff0c;所以&am…

软件测试/测试开发丨Vuetify框架的使用

介绍 Vuetify 是一个基于 Vue.js 精心打造 UI 组件库&#xff0c;整套 UI 设计为 Material 风格。能够让没有任何设计技能的开发者创造出时尚的 Material 风格界面。 为什么要使用Vuetify框架 所有组件遵从 Material Design 设计规范&#xff0c;UI 体验非常优秀&#xff0c…

zookeeper经典应用场景之分布式锁

1. 什么是分布式锁 在单体的应用开发场景中涉及并发同步的时候&#xff0c;大家往往采用Synchronized&#xff08;同步&#xff09;或者其他同一个JVM内Lock机制来解决多线程间的同步问题。在分布式集群工作的开发场景中&#xff0c;就需要一种更加高级的锁机制来处理跨机器的进…

MiniTab的宏基础知识

什么是宏&#xff1f; 宏是包含一系列 Minitab 会话命令的文本文件。可以使用宏自动执行重复性任务&#xff08;例如&#xff0c;生成月度报表&#xff09;或扩展 Minitab 的功能&#xff08;例如&#xff0c;计算特殊检验统计量&#xff09;。 Minitab 提供以下类型的宏&…

MongoDB索引详解

概述 索引是一种用来快速查询数据的数据结构。BTree 就是一种常用的数据库索引数据结构&#xff0c;MongoDB 采用 BTree 做索引&#xff0c;索引创建 colletions 上。MongoDB 不使用索引的查询&#xff0c;先扫描所有的文档&#xff0c;再匹配符合条件的文档。使用索引的查询&…

Spring IOC的四种手动注入方法

手动注入 1.Set方法注入-五种类型的注入1.1 业务对象JavaBean第一步&#xff1a;创建dao包下的UserDao类第二步&#xff1a;属性字段提供set⽅法第三步&#xff1a;配置⽂件的bean标签设置property标签第四步&#xff1a;测试 1.2 常用对象String&#xff08;日期类型&#xff…

每日一题——LeetCode1089.复写0

方法一 splice&#xff1a; 通过数组的slice方法&#xff0c;碰到 0就在后面加一个0&#xff0c;最后截取原数组的长度&#xff0c;舍弃后面部分。 但这样做是违反了题目的要求&#xff0c;不要在超过该数组长度的位置写入元素。 var duplicateZeros function(arr) {var le…

软件测试|全面解析Docker Start/Stop/Restart命令:管理容器生命周期的必备工具

简介 Docker是一种流行的容器化平台&#xff0c;用于构建、分发和运行应用程序。在使用Docker时&#xff0c;经常需要管理容器的生命周期&#xff0c;包括启动、停止和重启容器。本文将详细介绍Docker中的docker start、docker stop和docker restart命令&#xff0c;帮助您全面…

计算机组成原理-进位计数制(进制表示 进制转换 真值和机器树)

文章目录 现代计算机的结构总览最古老的计数方法十进制计数法推广&#xff1a;r进制计数法任意进制->十进制二进制<--->八进制&#xff0c;十六进制 各种进制常见的书写方式十进制->任意进制整数部分小数部分 十进制->二进制&#xff08;拼凑法&#xff09;真值…

Oracle OCP怎么样线上考试呢

大家好&#xff01;今天咱们就来聊聊Oracle OCP这个让人又爱又恨的认证。为啥说又爱又恨呢&#xff1f;因为它既是IT界的“金字招牌”&#xff0c;又是一块硬骨头&#xff0c;不是那么容易啃下来的。好了&#xff0c;废话不多说&#xff0c;我们直奔主题&#xff0c;来看看关于…

解决vue3中watch 监听不到旧值的问题,亲测有效!

问题描述 这个问题是我在公司vue3项目的时候发现的一个问题&#xff0c;watch 在监听对象/数组变量的变化时&#xff0c;发现对象的数据变化时 旧数据 获取到的和新数据是一样的 类似于下面这样 const objref({a:我是原来的值,b:6, })obj.a改变值watch(obj,(nel,old)>{ c…

studio3T mongodb 根据查询条件更新字段 或 删除数据

1. mongodb 等于、不等于$ne、不包含 $nin 以及批量更新数据的使用。 业务场景&#xff1a; 在集合中&#xff0c;根据查询条件&#xff0c;更新数据状态。 实现代码&#xff1a; 1. 部门名称为XXX、状态不等于“完好”的、并且不包含这些编码的数据先查询出来2. 再把状态更…