代码随想录算法训练营第七天|哈希表专题二
今日任务:
- leetcode 454 四数相加Ⅱ
- leetcode 383 赎金信
- leetcode 15 三数之和
- leetcode 18 四数之和
- 哈希表专题总结
leetcode 454 四数相加Ⅱ
题目链接:leetcode 454 四数相加
题目详情
思路
我们可以使用HashMap,其中key用来存储组合产生的和,value用来存储这个和出现的次数,为什么是存储次数呢?下面会讲到。那么具体步骤是什么呢,我们可以通过遍历nums1和nums2所有组合,将其所有产生的结果存入到HashMap中,然后遍历nums3,nums4所有产生的组合,如果HashMap中存在这个组合的相反数的话,那么说明这两个组合也就是这四个数相加为0,但是有可能这个相反数会重复出现,重复的组合也是一个元组,所有我们用value来存储组合出现的次数,当存在相反数时,即加上其value值即可。
代码
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
HashMap<Integer,Integer> map1=new HashMap<>();
//返回结果
int res=0;
//遍历两个数组所有的组合
for(int i=0;i<nums1.length;i++){
for(int j=0;j<nums2.length;j++){
int temp=nums1[i]+nums2[j];
//若已经存在需要将value+1
if(map1.containsKey(temp)){
map1.put(temp,map1.get(temp)+1);
}else{
//不存在就添加进去
map1.put(temp,1);
}
}
}
//遍历nums3,nums4两个数组的所有组合
for(int i=0;i<nums3.length;i++){
for(int j=0;j<nums4.length;j++){
int temp=nums3[i]+nums4[j];
//判断map中是否存在该组合的相反数,如果存在说明四数之和为0
if(map1.containsKey(-temp)){
//满足和为0,加上重复组合个数
res+=map1.get(-temp);
}
}
}
return res;
}
}
leetcode 383 赎金信
题目链接:leetcode 383 赎金信
题目详情
思路
本题跟上一篇链表专题一中的字母异位词解题思路差不多,字母异位词是判断两个字符串的字符组成是否完全一致,而本题是用来判断ransomNote串是否由magazine字符串中的字符构成。
解题思路还是一样,我们可以将magazine字符串中的字符全部存入到一个hash表中,在这里我们hash表我们可以使用数组来实现,将每个字符出现的次数记录下来。那么如何知道ransomNote串由magazine组成呢?
我们只需要遍历ransomNote串,将其出现的字符记录在数组中进行-1,那么最后数组元素为负数就是表明ransomNote中该字符少于magazine或者不存在该字符,那么说明magazine不是由ransomNote中的字符构成,若数组结果全部>=0,那么说明magazine中的字符全部存在于ransomNote中。
代码
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
int[] res =new int[26];
for(int i=0;i<magazine.length();i++){
char ch=magazine.charAt(i);
res[ch-'a']++;
}
for(int i=0;i<ransomNote.length();i++){
char ch=ransomNote.charAt(i);
if(res[ch-'a']==0){
return false;
}else{
res[ch-'a']--;
}
}
return true;
}
}
leetcode 15 三数之和
题目链接:leetcode 15 三数之和
题目详情
思路
本题归纳在哈希表章节,其实本题并不适用于哈希表的解法,更多的是使用双指针,双指针的特点就是可以将有序数组所有的可能全部遍历到,并且比暴力法降低一个数量级的时间复杂度。
对于本题我们应该怎么解决呢?首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。
然后再数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i],b = nums[left],c = nums[right]。
接下来如何移动left 和right呢, 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。
如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。
动图演示:
代码
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
//因为结果要求去重,不能重复,那么我们就使用Set去重
Set<List<Integer>> ResSet =new HashSet<>();
Arrays.sort(nums);
for(int i=0;i<nums.length-2;i++){
int left=i+1;
int right=nums.length-1;
while(left<right){
int temp=nums[i]+nums[left]+nums[right];
if(temp==0){
List<Integer> list =new ArrayList<>();
list.add(nums[i]);
list.add(nums[left]);
list.add(nums[right]);
ResSet.add(list);
//相等之后,左指针移动,继续判断。
left++;
}else if(temp>0){
right--;
}else{
left++;
}
}
}
//最后结果将Set转换为List返回
return new ArrayList<>(ResSet);
}
}
leetcode 18 四数之和
题目链接:leetcode 18 四数之和
题目详情:
思路
这题跟三数之和解题思路完全一样,无非就是前面两层for循环,外层循环i从0开始,内层循环j从i+1开始
letf从j+1开始,right从nums.length-1开始,还是先排序后使用双指针,时间复杂度为O(n^3),使用双指针降低一个数量级的时间复杂度。
代码
这里注意个细节,有个测试用例使用nums[i]+nums[j]+nums[left]+nums[right]会造成溢出,所以我们需要把结果向上转型,转成long,这样就可以防止出错了。
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
Set<List<Integer>> ResSet =new HashSet<>();
Arrays.sort(nums);
for(int i=0;i<nums.length-3;i++){
for(int j=i+1;j<nums.length-2;j++){
int left=j+1;
int right=nums.length-1;
while(left<right){
long temp=(long)nums[i]+nums[j]+nums[left]+nums[right];
if(temp==target){
List<Integer> list =new ArrayList<>();
list.add(nums[i]);
list.add(nums[j]);
list.add(nums[left]);
list.add(nums[right]);
ResSet.add(list);
left++;
}else if(temp>target){
right--;
}else{
left++;
}
}
}
}
return new ArrayList(ResSet);
}
}