Leecode 454. 四数相加II

题目描述

给你四个整数数组 nums1nums2nums3nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:
0 <= i, j, k, l < n
  • nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
  1. 示例 1:
  • 输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
  • 输出:2
  • 解释:
    • 两个元组如下:

      1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
      2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0
  1. 示例 2:
  • 输入:nums1 = [0], nums2 = [0], nums3 = [0], nums4 = [0]
  • 输出:1

解题思路与代码展示

考虑使用unordered_map容器,首先统计前两个数组求和得到的每个结果的数量,再对后两个数组求和,并取和的负数在容器中已经统计的数量求和。这样即可计算得到四个数组四数之和为0的数量。具体代码如下:

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; // 初始化满足条件的四数之和的数量为0
for(int num1 : nums1){
for(int num2 : nums2){
map[num1 + num2]++; // 计算前两个数组中求和后的结果并统计数量,将每个出现的求和作为键将其对应的值+1
}
}
for(int num3 : nums3){
for(int num4 : nums4){
if(map.find(-(num3+num4)) != map.end()) // 如果后两个数组求和结果的负值作为键能够在map容器中找到对应的值
count += map[-(num3+num4)]; // 则将该值进行累加计数
}
}
return count;
}
};

使用上面代码,即可计算得到四数之和为0的组合数量。分析上面代码,由于题目已经给定说明四个数组的长度都是\(n\),那么可以推算时间复杂度为\(O(n^2)\).

Leecode 383. 赎金信

题目描述

给你两个字符串:ransomNotemagazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。

如果可以,返回 true ;否则返回 false

magazine 中的每个字符只能在 ransomNote 中使用一次。

  • 示例 1:

    • 输入:ransomNote = "a", magazine = "b"
    • 输出:false
  • 示例 2:
    • 输入:ransomNote = "aa", magazine = "ab"
    • 输出:false
  • 示例 3:
    • 输入:ransomNote = "aa", magazine = "aab"
    • 输出:true

解题思路及代码展示

本题需要判断能否用字符串magazine中的字符组成ransomNote中的字符,一开始乍一看没反应过来这两个字符串的长度大小关系。后来突然注意到本题的标题“赎金信”对应字符串ransomNote,而字符串magazine表示杂志。只要联想场景绑匪为了不暴露自己的字迹,通过剪下杂志字符串中的字符来拼凑组成赎金信。那么这种情况下每个字符只能使用一次,即字符串ransomNote中的字符应该是真包含于字符串magazine中的。同时又需要注意这里并非表示集合,因为字符串中的字符是可以重复多次出现的。

在理解了题目之后,我们来思考如何解决这个问题。其实如果代入“绑匪”视角,为了判断能否组成赎金信,当然首先整理并统计一下目前杂志中每个字符都出现了多少次。随后根据赎金信中所需要用到字符逐一取出并使用。如果在需要某个字符的时候,整理统计好的字符中已经全部用完,那么则说明字符串不够。如果最终顺利组装完成了赎金信,则说明可以完成。

那么根据上面思想,我们可以写出下面代码:

class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
unordered_map<char,int> umap; // 初始化map键值对,可以根据字符类型的键在O(1)时间内搜索到存储的对应的int类型的值
for(int i = 0; i < magazine.size(); i++){ // 遍历杂志中所有的字符
umap[magazine[i]]++; // 将统计所有的字符,每个字符对应的数量+1
}
for(int i = 0; i < ransomNote.size(); i++){ // 遍历赎金信
if(umap[ransomNote[i]]-- <= 0) return false; // 赎金信每用一个字符就将统计的键值对中对应的值-1;如果此时值小于等于0,则说明不够,返回false
}
return true; // 如果遍历完赎金信都还没有出现字符不够的情况,则说明可以组成赎金信,返回true
}
};

上面代码可以看出,时间复杂度在\(O(m+n)\),即将两个字符串各遍历一遍即可完成这个算法。

Leecode 15. 三数之和

题目描述

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

  • 示例 1:

    • 输入:nums = [-1,0,1,2,-1,-4]
    • 输出:[[-1,-1,2],[-1,0,1]]
    • 解释:
      • nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0
      • nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0
      • nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0
      • 不同的三元组是 [-1,0,1][-1,-1,2]
      • 注意,输出的顺序和三元组的顺序并不重要。
  • 示例 2:
    • 输入:nums = [0,1,1]
    • 输出:[]
    • 解释:唯一可能的三元组和不为 0
  • 示例 3:
    • 输入:nums = [0,0,0]
    • 输出:[[0,0,0]]
    • 解释:唯一可能的三元组和为 0

解法一,暴力求解

首先是一个使用四层for循环的,很纯粹的暴力解。即先对数组进行排序,再遍历三个指针的所有可能的不重复的取值,如果三个值求和相等则取出。取出之后再对已经找到的解矩阵中使用一层for循环来判断当前数组是否重复。如果重复则丢弃,不重复则放入当前结果矩阵中。

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()-2; i++){ // 第一个指针i取值从0到长度-3
for(int j = i + 1; j < nums.size()-1; j++){ // 第二个指针 j 取值从 i + 1到长度-2
for(int k = j + 1; k < nums.size(); k++){ // 第三个指针 k 取值从 j + 1到长度-1
if(nums[i]+ nums[j] + nums[k] == 0) { // 如果三数之和满足条件
bool exist = false; // 初始化当前值是否重复的布尔变量
vector<int> newVec({nums[i], nums[j], nums[k]}); // 先将当前三个值组成vector数组
for(int l = 0; l < result.size(); l++){ // 逐一判断已经找到的解集,是否已经有当前找到的vector
if(newVec == result[l]) exist = true; // 如果已经找到,则更改exist为true
}
if(exist) continue; // 如果当前vector已经找到,则直接继续循环查找其他值
result.push_back(newVec); // 如果当前vector并不存在与解集中,则将其放入解集
}
}
}
}
return result;
}
};

上面算法最差情况下的时间复杂度为\(O(n^4)\),最好情况也有\(O(n^3)\)。都是非常高的时间复杂度。而且该算法也不能通过Leecode网站的所有测试用例(因为会超时)。因此我们再考虑使用更高效的算法。

解法二,双指针

双指针法根据已知过大还是过小的信息来确定指针查找的方向,以此来降低搜索满足条件的解的算法的时间复杂度。

class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result; // 初始化解集
sort(nums.begin(),nums.end()); // 对输入的vector进行排序
for(int a = 0; a < nums.size() - 2; a++){ // 遍历第一个指针a可能取到从0到n-3
if(a > 0 && nums[a] == nums[a-1]) continue;
// 除了当前a在初始起点的时候之外,如果当前值和上一个值相同,说明后续找到的另外两个指针对应的值也相同,故直接跳过 int left = a + 1; // left指针起始值从当前a的下一个开始开始往右搜索
int right = nums.size() - 1; // right指针从最右侧往左搜索 while(left < right){ // 当left指针和right指针还未相碰时,则一直进行循环
int sum2 = nums[left] + nums[right]; // 对后两个指针对应的值求和
if( sum2 == -nums[a]) { // 如果求和的值是a对应的值的相反数,则说明求和为0
result.push_back(vector<int>({nums[a], nums[left++], nums[right-- ]})); // 则将当前数组记录,同时左右指针都需要移动一位
while(left < nums.size() && nums[left] == nums[left-1]) left++; // 如果左右指针在刚移动了一位之后,当前值和上一步的值还相同,需要一直继续移动
while(right > left && nums[right] == nums[right+1]) right--; // 直至左右指针都找到一个新的取值时再进入下一次循环
}
else if(sum2 > -nums[a]) right--; // 如果当前三个值求和大于0,说明right指针需要减小
else left++; // 如果当前三个值求和小于0,说明left指针需要变大
}
}
return result;
}
};

使用上面算法,即可在去重的前提下,找到所有的三数之和为0的组合。时间复杂度为\(O(n^2)\).

Leecode 18. 四数之和

题目描述

  • 给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  • 0 <= a, b, c, d < n

  • abcd 互不相同

  • nums[a] + nums[b] + nums[c] + nums[d] == target

    你可以按 任意顺序 返回答案 。

  • 示例 1:

    • 输入:nums = [1,0,-1,0,-2,2], target = 0
    • 输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
  • 示例 2:

    • 输入:nums = [2,2,2,2,2], target = 8
    • 输出:[[2,2,2,2]]

解题思路与代码展示

本题思路和上一题非常类似,都是使用双指针,同时根据当前求和偏大还是偏小来相应地调整两个指针的搜索方向。以此来减少所需要遍历的可能解的数量。但和上一题的区别在于,需要多加一层循环,以此来表示多一个指针的可能取值。

class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result; // 定义result作为解集存放所有解
if(nums.size() < 4) return result; // 如果当前长度小于4,则四数之和无意义,直接返回result
sort(nums.begin(),nums.end()); // 对vector容器进行排序
for(int a = 0; a < nums.size() - 3; a++){ // 第一个指针a的取值从0到数组长度n-4
if(a != 0 && nums[a] == nums[a-1]) continue; // 避免重复查找,如果第一个指针在移动过后,和上一次的数值还相同,则需要继续移动
for(int b = a+1; b < nums.size() - 2; b++){ // 第二个指针b的取值从a+1到数组长度n-3
if(b != a+1 && nums[b] == nums[b-1]) continue; // 同样,如果指针b在移动过后,和上一次的数组还相同,则需要继续移动
int left = b + 1; // 左指针可取的最小值为b+1
int right = nums.size()-1; // 右指针可取值为数组中最后一位,即长度n-1
while(left < right){ // 只要左右指针还未相碰,则一直遍历搜索
long sum = (long)nums[a] + (long)nums[b] + (long)nums[left] + (long)nums[right]; // 计算四数之后,此时int类型会溢出,因此转换为long类型
if(sum == target){ // 如果求和满足条件
result.push_back(vector<int>({nums[a],nums[b],nums[left++],nums[right--]})); // 将找到的数组成数组存放入解集,并同时移动left和right指针
while(left < right && nums[right] == nums[right+1]) right--; // 如果左右指针移动过后,取值还和刚才相等,需要一直移动到值不相等
while(left < right && nums[left] == nums[left-1]) left++;
}
else if(sum > target) right--; // 如果四数之和太大,则需要令right指针减小
else left++; // 如果四数之后太小,则需要让left指针变大
}
}
}
return result;
}
};

本题上面算法之比上一题多了一层for循环,其余过程都非常相近。因此可以知道时间复杂度为\(O(n^3)\).

今日总结

今天继续学习了哈希表部分的题目,更进一步熟悉了unordered_set容器和unordered_map容器。

代码随想录第七天 | Leecode 454.四数相加II 、383. 赎金信 、15. 三数之和 、18. 四数之和的更多相关文章

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

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

  2. 【算法训练营day7】LeetCode454. 四数相加II LeetCode383. 赎金信 LeetCode15. 三数之和 LeetCode18. 四数之和

    [算法训练营day7]LeetCode454. 四数相加II LeetCode383. 赎金信 LeetCode15. 三数之和 LeetCode18. 四数之和 LeetCode454. 四数相加I ...

  3. 代码随想录第七天| 454.四数相加II、383. 赎金信 、15. 三数之和 、18. 四数之和

    第一题454.四数相加II 给你四个整数数组 nums1.nums2.nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足: 0 <= i, ...

  4. Java实现 LeetCode 454 四数相加 II

    454. 四数相加 II 给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0. 为 ...

  5. Leetcode 454.四数相加II

    四数相加II 给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0. 为了使问题简单 ...

  6. 【哈希表】leetcode454——四数相加II

    编号454:四数相加II 给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0. 为 ...

  7. 四数相加II & 赎金信 & 三数之和 & 四数之和

    一.四数相加Ⅱ 454. 四数相加 II 1.方法概述 首先定义一个map,key放a和b两数之和,value 放a和b两数之和出现的次数.遍历大A和大B数组,统计两个数组元素之和,和出现的次数,放到 ...

  8. LeetCode454. 四数相加 II

    题目 给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0. 分析 关键是如何想到用 ...

  9. 454. 四数相加 II

    给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0. 为了使问题简单化,所有的 A ...

  10. LeetCode 454.四数相加 II(C++)

    给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0. 为了使问题简单化,所有的 A ...

随机推荐

  1. C# TorchSharp 图像分类实战:VGG大规模图像识别的超深度卷积网络

    目录 图像分类 | VGG大规模图像识别的超深度卷积网络 数据集 直接下载 opendatalab 数据集社区 自定义数据集 模型训练 教程名称:使用 C# 入门深度学习 作者:痴者工良 教程地址: ...

  2. Typora Emoji图标

    转自: https://www.cnblogs.com/wangjs-jacky/p/12011208.html People  :smile:  :laughing:    :blush:  :sm ...

  3. Typecho如何添加微博表情包

    自从添加了蛆音娘表情包就想着去爬点其他地方的表情包- 使用教程跟蛆音娘一样 :点我查看 #表情包代码: "微博":{ "type": "usr&quo ...

  4. NCS开发学习笔记-基础篇-第 1 课 – nRF Connect SDK 简介

    第 1 课 – nRF Connect SDK 简介 目标 了解 nRF Connect SDK 的结构和内容 在内部,nRF Connect SDK 代码分为四个主要存储库: nrf – 应用程序. ...

  5. PHP对表单提交特殊字符的过滤和处理方法汇总

    http://www.jb51.net/article/46921.htm PHP关于表单提交特殊字符的处理方法做个汇总,主要涉及htmlspecialchars/addslashes/stripsl ...

  6. 原生开发,使用C语言调用Windows API 开发软件思路分享

    Githu: https://github.com/vladelaina/Catime 作者是一个高度依赖计时器功能的人,但是市面上的软件都不能满足个性化的需求,所以打算自己动手开发,同时采用c语言来 ...

  7. helm Error: INSTALLATION FAILED: cannot re-use a name that is still in use

    前言 使用helm安装服务报错,修改chat后重新安装报错:安装失败:无法重复使用仍在使用的名称 解决方法 1.查找安装失败的服务 helm -n {namespace} ls -a 2.删除安装失败 ...

  8. 关闭windows计划重启

    前言 windows 总是自动计划更新 解决方案 需要禁用服务 "Windows Update" 和 "更新 Orchestrator 服务" 首先去这里下载P ...

  9. golang 使用goto进行多错误处理

    goto 语句介绍 在 Go 语言中,可以通过goto语句跳转到标签,进行代码间的无条件跳转.另外,goto语句在快速跳出循环.避免重复退出方面可以简化代码实现过程,但在结构化程序设计中一般不主张使用 ...

  10. Django实战项目-学习任务系统-发送短信通知

    接着上期代码内容,继续完善优化系统功能. 本次增加发送短信通知功能,学习任务系统发布的任务,为了更加及时通知到学生用户,再原有发送邮件通知基础上,再加上手机短信通知功能. 第一步:开通短信通知服务 目 ...