代码随想录算法训练营第七天|哈希表专题二

代码随想录算法训练营第七天|哈希表专题二

今日任务:

  • 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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Janice0721

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值