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

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

1. LeetCode 454.四数相加II

1.1 思路

  1. 这题不需要考虑去重并且不需要知道具体哪4个数,求出数量count即可
  2. 遍历A、B数组把a+b的值放入集合的key中并且统计a+b出现过几次然后放入集合的value中,在遍历C、D数组时判断集合里是否有想要的元素(找匹配项),因此用map
  3. 遍历C、D数组时从map中查找0-(a+b)的值出现过几次
  4. 遍历到的次数后count+什么呢?应该是加(a+b)的value值
  5. 为什么不先遍历A数组然后再遍历BCD三个数组呢?如果是这种方式遍历A时复杂度是N,遍历BCD时复杂度是N^3,这样总的时间复杂度是N^3。而上述的先遍历AB是N^2,再遍历CD是N^2,这样总的时间复杂度是N^2

1.2 代码

class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        int res = 0;
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        //统计两个数组中的元素之和,同时统计出现的次数,放入map
        for (int i : nums1) {
            for (int j : nums2) {
                int sum = i + j;
                map.put(sum, map.getOrDefault(sum, 0) + 1);
            }
        }
        //统计剩余的两个元素的和,在map中找是否存在相加为0的情况,同时记录次数
        for (int i : nums3) {
            for (int j : nums4) {
                res += map.getOrDefault(0 - i - j, 0);
            }
        }
        return res;
    }
}

2. LeetCode 383. 赎金信

2.1 思路

  1. 因为题目说只有小写字母,那可以采用空间换取时间的哈希策略,用一个长度为26的数组来记录magazine里字母出现的次数。
  2. 然后再用ransomNote去验证这个数组是否包含了ransomNote所需要的所有字母。
  3. 你可能想,用数组干啥,都用map完事了,其实在本题的情况下,使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!

2.2 代码

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        // shortcut
        if (ransomNote.length() > magazine.length()) {
            return false;
        }
        // 定义一个哈希映射数组
        int[] record = new int[26];
        // 遍历
        for(char c : magazine.toCharArray()){
            record[c - 'a'] += 1;
        }
        for(char c : ransomNote.toCharArray()){
            record[c - 'a'] -= 1;
        }
        // 如果数组中存在负数,说明ransomNote字符串总存在magazine中没有的字符
        for(int i : record){
            if(i < 0){
                return false;
            }
        }
        return true;
    }
}

3. LeetCode 15. 三数之和

3.1 思路

  1. 求三数之和为0,这个三元组要去重(难点)用双重for循环比那里寻找a+b的值,存放到map中,然后让寻求0-(a+b)的值是否在map出现过,但这样去重比较复杂。
  2. 双指针法:我们将数组排序后,用for循环遍历i的位置表示a,然后在i+1的位置用left表示b,arr.length-1的位置用right表示c,然后用nums[i]+nums[left]+nums[right]求和
  3. 如果>0,则让right--,因为我们是排好序的,最右边的是最大的,此时>0,所以让最大的数变小,才能让和变小
  4. 如果<0,则让left++,因为我们是排好序的,最左边的是最小的,此时<0,所以让最小的数变大,才能让和变大
  5. 如果=0,则把这三个位置的数放入二维数组result中
  6. 细节在去重:abc三个数都是要去重,如果排好序后,nums[i]就>0就直接return,因为这是三元组里第一个数,都大于0就没法和为0了
  7. 去重a:nums[i]==nums[i+1]这种去重方式是判断了结果集里是否有相同元素,但结果集是可以有相同元素的;nums[i]==nums[i-1]这种去重方式是判断了i的前一个里是否有相同元素,这样判断的话是说明这两个元素如果已经用到了,就不能再用了,直接continue跳过,这样才对,并且这里要加个i>0&&的条件,避免出现下标为负的情况
  8. 去重b、c:因为left=i+1,right=arr.length-1;while(right>left)这里不要等于号,有等于号的话就是两个指针指向同个数了,那就少了一个数了
  9. while(right>left&&nums[right]==nums[right-1])那就继续right--while(right>left&&nums[left]==nums[left+1])那就继续left++

3.2 代码

class Solution {
    public 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++) {
      // 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了
            if (nums[i] > 0) { 
                return result;
            }
            if (i > 0 && nums[i] == nums[i - 1]) {  // 去重a
                continue;
            }
            int left = i + 1;
            int right = nums.length - 1;
            while (right > left) {
                int sum = nums[i] + nums[left] + nums[right];
                if (sum > 0) {
                    right--;
                } else if (sum < 0) {
                    left++;
                } else {
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));
        // 去重逻辑应该放在找到一个三元组之后,对b 和 c去重
                    while (right > left && nums[right] == nums[right - 1]) right--;
                    while (right > left && nums[left] == nums[left + 1]) left++;
                    right--; 
                    left++;
                }
            }
        }
        return result;
    }
}

4. LeetCode 18. 四数之和

4.1 思路

  1. 这里延续了三数之和的思路,同理第一个位置k(第一个for循环),下一个位置i(嵌套在第一个for循环),再下一个位置left,最后一个位置right,并且先排好序
  2. 对第一个数剪枝:如果nums[k]>target&&nums[k]>0&&target>0,这样才可以break,因为如果两个都为负数的情况,比如四个数为{-4,-1,0,0},target是5,这样的话如果不满足上述条件,就错过这个结果集了
  3. 对第一个数去重:if(k>0&&nums[k]==nums[k-1]那就continue跳过
  4. 对第二个数剪枝:此时将第一、二个数字作为一个整体,如果nums[i]+nums[k]>target&&target>0,这样也可以break
  5. 对第二个数去重:if(i>k+1&&nums[i]==nums[i-1]那就continue跳过,因为i是在k后一个位置的
  6. 下面的去重就是上面三数之和一样的了

4.2 代码

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);
        for (int i = 0; i < nums.length; i++) {
            // nums[i] > target 直接返回, 剪枝操作
            if (nums[i] > 0 && nums[i] > target) {
                return result;
            }
            if (i > 0 && nums[i - 1] == nums[i]) {    // 对nums[i]去重
                continue;
            }
            for (int j = i + 1; j < nums.length; j++) {
                if (j > i + 1 && nums[j - 1] == nums[j]) {  // 对nums[j]去重
                    continue;
                }
                int left = j + 1;
                int right = nums.length - 1;
                while (right > left) {
        // nums[k] + nums[i] + nums[left] + nums[right] > target int会溢出
                    long sum = (long) nums[i] + nums[j] + nums[left] + nums[right];
                    if (sum > target) {
                        right--;
                    } else if (sum < target) {
                        left++;
                    } else {
                        result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
                        // 对nums[left]和nums[right]去重
                        while (right > left && nums[right] == nums[right - 1]) right--;
                        while (right > left && nums[left] == nums[left + 1]) left++;
                        left++;
                        right--;
                    }
                }
            }
        }
        return result;
    }
}
相关文章
|
1月前
|
算法
Leetcode 初级算法 --- 数组篇
Leetcode 初级算法 --- 数组篇
38 0
|
7天前
|
算法
分享一些提高二叉树遍历算法效率的代码示例
这只是简单的示例代码,实际应用中可能还需要根据具体需求进行更多的优化和处理。你可以根据自己的需求对代码进行修改和扩展。
|
10天前
|
存储 算法 Java
leetcode算法题-有效的括号(简单)
【11月更文挑战第5天】本文介绍了 LeetCode 上“有效的括号”这道题的解法。题目要求判断一个只包含括号字符的字符串是否有效。有效字符串需满足左括号必须用相同类型的右括号闭合,并且左括号必须以正确的顺序闭合。解题思路是使用栈数据结构,遍历字符串时将左括号压入栈中,遇到右括号时检查栈顶元素是否匹配。最后根据栈是否为空来判断字符串中的括号是否有效。示例代码包括 Python 和 Java 版本。
|
18天前
|
算法 测试技术 开发者
在Python开发中,性能优化和代码审查至关重要。性能优化通过改进代码结构和算法提高程序运行速度,减少资源消耗
在Python开发中,性能优化和代码审查至关重要。性能优化通过改进代码结构和算法提高程序运行速度,减少资源消耗;代码审查通过检查源代码发现潜在问题,提高代码质量和团队协作效率。本文介绍了一些实用的技巧和工具,帮助开发者提升开发效率。
19 3
|
17天前
|
分布式计算 Java 开发工具
阿里云MaxCompute-XGBoost on Spark 极限梯度提升算法的分布式训练与模型持久化oss的实现与代码浅析
本文介绍了XGBoost在MaxCompute+OSS架构下模型持久化遇到的问题及其解决方案。首先简要介绍了XGBoost的特点和应用场景,随后详细描述了客户在将XGBoost on Spark任务从HDFS迁移到OSS时遇到的异常情况。通过分析异常堆栈和源代码,发现使用的`nativeBooster.saveModel`方法不支持OSS路径,而使用`write.overwrite().save`方法则能成功保存模型。最后提供了完整的Scala代码示例、Maven配置和提交命令,帮助用户顺利迁移模型存储路径。
|
1月前
|
存储 缓存 算法
如何通过优化算法和代码结构来提升易语言程序的执行效率?
如何通过优化算法和代码结构来提升易语言程序的执行效率?
|
1月前
|
算法
每日一道算法题(Leetcode 20)
每日一道算法题(Leetcode 20)
23 2
|
1月前
|
搜索推荐
插入排序算法的讲解和代码
【10月更文挑战第12天】插入排序是一种基础的排序算法,理解和掌握它对于学习其他排序算法以及数据结构都具有重要意义。你可以通过实际操作和分析,进一步深入了解插入排序的特点和应用场景,以便在实际编程中更好地运用它。
|
3月前
|
机器学习/深度学习 人工智能 自然语言处理
【自然语言处理】TF-IDF算法在人工智能方面的应用,附带代码
TF-IDF算法在人工智能领域,特别是自然语言处理(NLP)和信息检索中,被广泛用于特征提取和文本表示。以下是一个使用Python的scikit-learn库实现TF-IDF算法的简单示例,并展示如何将其应用于文本数据。
257 65
|
23天前
|
缓存 分布式计算 监控
优化算法和代码需要注意什么
【10月更文挑战第20天】优化算法和代码需要注意什么
17 0