哈希表part02

1.四数相加II

题目链接:四数相加II
区别于常见的3-sum、4-sum,四个数是存在于4个不同的数组中的,所以直接使用哈希法。考虑将4个数组分为2组,计算2个数组合后转换为传统的2-sum问题。
✅ 一些性能提升的总结:
【1】在遍历数组时,如果在循环体内不需要使用索引
增强型 for 循环 for(int i : nums1){}
比起传统for循环 for(int i = 0; i < nums1.length; i++){}
微快一点,因为它避免了显式索引的使用
【2】当涉及到从Map中获取与特定键关联的值时
直接调用getOrDefault(target, 0)
getOrDefault 方法比使用 containsKey 后跟 get 的组合更快,因为它只涉及一次映射访问

class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        Map<Integer,Integer> sum_map1=new HashMap<>();
        int count=0;
        for(int i : nums1){
             for(int j:nums2){
                int sum1=i+j;
                sum_map1.put(sum1, sum_map1.getOrDefault(sum1, 0)+1);
            }
        }
        for(int i:nums3){
             for(int j:nums4){
                int target=0-i-j;
                count+=sum_map1.getOrDefault(target, 0);
             }
        }
        return count;
    }
}

2.赎金信

题目链接:赎金信
这道题目是242.有效的字母异位词的变体。相当于求字符串a能否组成字符串b。涉及到字符串且提到题目说只有小写字母,想到用数组来当简单哈希表存储字母出现的次数。

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        char[] magazine_array=magazine.toCharArray();
        char[] note_array=ransomNote.toCharArray();
        int[] dic_array=new int[26];
        for(char c : magazine_array){
            dic_array[c-'a']++;
        }
        for(char c : note_array){
            if(dic_array[c-'a']==0){
                return false;
            }
            dic_array[c-'a']--;
        }
        return true;
    }
}

3.三数之和

题目链接:三数之和
3sum问题,3个数字取自同一数组,要求返回索引的list且不重复。首先尝试使用类似2sum的哈希方法,先用两层for循环求出两数之和(a+b),再用0-(a+b)找出第三数,难点在于去重的问题,较为复杂
考虑使用双指针的方法:
(1)先将数组排序。✅ Arrays.sort()是将数组元素从小到大排列
(2)选取第一个数字a的过程作为外层for循环。✅ 剪枝:当a>0时,b+c一定>0,不可能再出现三数和为0,直接返回结果
(3)选第二个b为左指针(初始化为第一个数字+1),第三个数字c为右指针(初始化为最右)。✅ 去重:排序后的a,b,c均存在去重的问题,如果重复指针需要继续移动
(4)a+b+c<0时,左指针右移;a+b+c>0时,右指针左移;

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> re=new ArrayList<>();
        //排序
        Arrays.sort(nums);
        int left,right,target,sum;
        int addedI=nums[0]-1;
        for(int i =0; i<nums.length; i++){
            if(nums[i]==addedI){
                continue;
            }
            left=i+1;
            addedI=nums[i];
            right=nums.length-1;
            target=0-nums[i];
            while(left<right){
                sum=nums[right]+nums[left];
                if(sum>target){
                   right--; 
                }
                else if(sum<target){
                    left++;
                }
                else{
                    List<Integer> list = new ArrayList<>();
                    list.add(nums[i]);      
                    list.add(nums[left]);   
                    list.add(nums[right]);
                    re.add(list);
                    int addedLeft=nums[left];
                    int addedRight=nums[right];
                    while(left<nums.length && nums[left]==addedLeft){
                        left++;
                    }
                    while(right>=0 && nums[right]==addedRight){
                        right--;
                    }
                }
            }
        }
        return re;
    }
}

4.四数之和

题目链接:四数之和
同3sum问题,只是将选取前两个数字作为外层2层for循环。
剪枝问题较为复杂,需谨慎
(1)仅当第一个数字 a>target 时还不能结束返回result,因为有可能target是个负数,需再加上a>0或target>0的条件
(2)第二个数字b的剪枝,就不是结束返回result,只是break,同样需要加上target>0的条件。

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        //排序
        Arrays.sort(nums);
        List<List<Integer>> result=new ArrayList<>();
        int right,left;
        long sum1,sum2;
        for(int i=0;i<nums.length;i++){
            if(nums[i]>0 && nums[i]>target){
                return result;
            }
            //去重
            if(i>0 && nums[i]==nums[i-1]){
                continue;
            }
            for(int j=i+1;j<nums.length;j++){
                sum1=nums[i]+nums[j];
                //剪枝
                if(sum1>0 && sum1>target){
                    break;
                }
                //去重
                if(j>i+1 && nums[j]==nums[j-1]){
                    continue;
                }
                left=j+1;
                right=nums.length-1;
                while(left<right){
                    sum2=sum1+nums[left]+nums[right];
                    if(sum2>target){
                        right--;
                    }
                    else if(sum2<target){
                        left++;
                    }
                    else{
                        result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
                        int addedLeft=nums[left];
                        int addedRight=nums[right];
                        while(left<right && nums[left]==addedLeft){
                            left++;
                        }
                        while(left<right && nums[right]==addedRight){
                            right--;
                        }
                    }
                }

            }
        }
        return result;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值