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

文章介绍了使用哈希法解决四数相加问题,强调了哈希表在优化时间复杂度上的作用,以及在赎金信问题中通过计数字符出现次数判断字符串能否构成的方法。同时,详细阐述了双指针法在解决三数之和及四数之和问题中的应用,强调了排序、去重和边界条件处理的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 454.四数相加II 

建议:本题是 使用map 巧妙解决的问题,好好体会一下 哈希法 如何提高程序执行效率,降低时间复杂度,当然使用哈希法 会提高空间复杂度,但一般来说我们都是舍空间 换时间, 工业开发也是这样。

题目链接/文章讲解/视频讲解:代码随想录

常用的 `Map` 接口方法:

1. `V get(Object key)`:返回指定键所映射的值,如果键不存在,则返回 `null`。

2. `V put(K key, V value)`:将指定的键值对添加到 `Map` 中,如果键已经存在,则会用新值替换旧值,并返回旧值。

3. `boolean containsKey(Object key)`:判断 `Map` 是否包含指定的键。

4. `boolean containsValue(Object value)`:判断 `Map` 是否包含指定的值。

5. `int size()`:返回 `Map` 中键值对的数量。

6. `V remove(Object key)`:从 `Map` 中删除指定键对应的键值对,并返回该键对应的值。

7. `void clear()`:清空 `Map` 中的所有键值对。

8. `Set<K> keySet()`:返回包含所有键的集合。

9. `Collection<V> values()`:返回包含所有值的集合。

10. `Set<Map.Entry<K, V>> entrySet()`:返回包含所有键值对的集合。

11. `default V getOrDefault(Object key, V defaultValue)`:返回指定键所映射的值,如果键不存在,则返回一个默认值。

12. `default void forEach(BiConsumer<? super K, ? super V> action)`:对 `Map` 中的每个键值对执行指定的操作。

这些 `Map` 接口中常用的方法,可以用于对键值对进行增、删、改、查等操作。

public static int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
//        不仅要考虑a+b在这个集合里有没有出现过,还要统计a+b出现过多少次,所以用map
//        key:a+b的数值 value:a+b数值出现的次数
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int a : nums1) {
            for (int b : nums2) {
                 int sum=a+b;
//                V getOrDefault(Object key,V defaultValue)
//                key:要获取键的值
//                defaultValue:如果键不存在,则返回的默认值
//                getOrDefault()是map接口中的一个方法,用于获取指定键对应的值,如果键不存在,则返回一个默认的值

//                将sum作为key,出现的次数作为value,保存在map中
                 map.put(sum, map.getOrDefault(sum, 0)+1);
            }
        }
//        计算c+d元素的和,并在map中查找其相反数的出现次数
        int count=0;
        for (int c : nums3) {
            for (int d : nums4) {
                int sum=c+d;
//                如果相反数在map中存在,则将其出现次数累加到count变量中
                count+=map.getOrDefault(-sum,0);
            }
        }
        return count;
    }

 383. 赎金信  

建议:本题 和 242.有效的字母异位词 是一个思路 ,算是拓展题 

题目链接/文章讲解:代码随想录

 对表格特性解释:

1.可变性:StringBuilder和StringBuffer是可变的,可以通过方法修改其中的内容,而String是不可变的,一旦创建就不能被修改。

2.线程安全性:StringBuilder是非线程安全的,不适合在多线程下使用。StringBuffer和String是线程安全的,可以在多线程环境下使用。

3.性能:StrignBuilder的性能最高,因为它不需要进行头同步操作。String的性能最低,因为每次修改都会创建一个新的String对象。String的性能居中,因为它需要进行同步操作,但比String要快。

4.内存使用:StringBuilder的内存使用较低,因为它不需要创建新的对象,String的内存使用较高,因为每次修改都会创建新的String对象。

5.使用场景:SrtingBuilder适用于单线程环境下需要频繁修改字符串的场景,String适用于无需频繁修改字符串的场景,StringBuffer适用于多线程环境下需要频繁修改字符串的场景。

思路①:暴力法

两层for循环遍历,如果a字符串中和b字符串中的字符相等,就删掉a(子)字符串中的字符。

如果删到最后a字符串中的长度为0咯就说明::a字符串中的字符在b中都有能与之匹配的。

public static boolean canConstruct(String ransomNote, String magazine) {
        StringBuilder sb = new StringBuilder(ransomNote);
        for (int i = 0; i < magazine.length(); i++) {
            for (int j = 0; j < sb.length(); j++) {
//                在ransomNote中找到和magazine相同的字符
                if(magazine.charAt(i)==sb.charAt(j)){
//                    substring()可以从原始字符串中提取出指定位置的子字符串
                    sb.deleteCharAt(j);//删除这个字符
                    break;
                }
            }
        }
        if(sb.length()==0){
           return true;
        }
        return false;
    }

思路②:哈希法

遍历a字符串

hash[c-'a']+=1;

遍历b(子)字符串

hash[c-'a']-=1;

如果到最后b字符串有很多负数,就说明a中不存在或者少一些与b相对于的字符,我们这道题要求的是a的字符串囊括的要多些?所以最后结果小于0就说明不正确。

public static boolean canConstruct(String ransomNote, String magazine) {
//        数组在哈希法中的应用
//        用一个长度为26的数组记录magazine里字母出现的次数
        int[] hash = new int[26];

//        再用ransomNote去验证这个数组是否包含了ransomNote所需要的所有字母
//        toCharArray()将字符串转换为字符数组
        for (char c : magazine.toCharArray()) {
            hash[c-'a']+=1;
        }
        for(char c: ransomNote.toCharArray()){
            hash[c-'a']-=1;
        }
//        如果字符串存在负数,说明randomNote字符串中总存在magazine中没有的字符
        for(int i: hash){
            if(i<0){
                return false;
            }
        }
        return true;
}

 15. 三数之和 

建议:本题虽然和 两数之和 很像,也能用哈希法,但用哈希法会很麻烦,双指针法才是正解,可以先看视频理解一下 双指针法的思路,文章中讲解的,没问题 哈希法很麻烦。 

题目链接/文章讲解/视频讲解:代码随想录

问题:给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?

思路:双指针法

收集的结果集里不能有重复的三元组,所有对a,b,c进行去重。 对数组进行排序。

第一个i的元素的位置是不变的,将left定义在i后的位置,而right定义在最后的位置。要求的结果是nums[i]+nums[left]+nums[right]=0 。如果nums[i]+nums[left]+nums[right]>0,就把right--,向前移动一位,nums[i]+nums[left]+nums[right]<0,就把left++,向后移动,这就体现了排序的好处。

要求的是不能重复的三元组,但三元组里面的元素是可以重复的。

 这两种写法截然不同。

边界问题的判断。如果left==right的话,不满足三元组的条件。

 去重的逻辑,一定要放在收获结果的下面。至少要收获符合一个条件的结果。

public static List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);
//        找出a+b+c=0
//        a=nums[i],b=nums[left],c=nums[right]
        for (int i = 0; i < nums.length; i++) {
    //            排序之后如果第一个元素已经>0,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了
            if(nums[i]>0){
                return result;
            }
    //            去重a
            if (i>0 && nums[i]==nums[i-1]){
                continue;
            }
            int left=i+1;
            int right=nums.length-1;
            while (left<right){
                int sum=nums[i]+nums[left]+nums[right];
                if(sum>0){
                    right--;
                }else if(sum<0){
                    left++;
                }else {
//                    asList():将指定数组转换为List
                    result.add(Arrays.asList(nums[i],nums[left],nums[right]));
//                    去重逻辑应该放在一个三元组之后,对b和c去重
//                    通过while循环移动指针到下一个不重复的位置
                    while (right>left && nums[right]==nums[right-1]) right--;
                    while (right>left && nums[left]==nums[left+1]) left++;
//                    把重复的删了移到原本想要的位置,原本想要的位置应该就是结果考虑了去重的。
                    right--;
                    left++;
                }
            }
         }
    return result;
    }

18. 四数之和  

建议: 要比较一下,本题和 454.四数相加II 的区别,为什么 454.四数相加II 会简单很多,这个想明白了,对本题理解就深刻了。 本题 思路整体和 三数之和一样的,都是双指针,但写的时候 有很多小细节,需要注意,建议先看视频。 

题目链接/文章讲解/视频讲解:代码随想录

思路:和三数之和相同,就多了一层for循环

public static List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> list = new ArrayList<>();
        Arrays.sort(nums);
        for (int k = 0; k < nums.length; k++) {
            if(nums[k]> target && target>0){
                break;
            }
            if (k>0 && nums[k]==nums[k-1]){
                continue;
            }
            for (int i = k+1; i < nums.length; i++) {
                if(nums[k]+nums[i]>target && target>0){
                    break;
                }
                if(i>k+1 && nums[i]==nums[i-1]){
                    continue;
                }
                int left=i+1;
                int right=nums.length-1;
                while (left<right){
                    long sum=(long)nums[k]+nums[i]+nums[left]+nums[right];
                    if(sum >target){
                        right--;
                    }else if(sum<target){
                        left++;
                    }else {
                        list.add(Arrays.asList(nums[k], nums[i], nums[left], nums[right]));
                        while (left<right && nums[left]==nums[left+1]) left++;
                        while (left<right && nums[right]==nums[right-1]) right--;
                        left++;
                        right--;
                    }
                }
            }
        }
        return list;
    }

总结

四数相加:哈希法。key放两数之和,value放a和b两数之和出现的次数。两个双层for循环。

赎金信:和字母异位词相同,定义一个数组记录字符串出现的次数。一个字符串对数组+1,另一个字符串-1,如果最后数组里面的元素有负数,说明大数组的元素没有包含小数组的元素,小数组不能由大数组组成。

三/四数之和:双指针法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值