找回密码
 立即注册
首页 业界区 科技 算法day06-哈希表篇(2)

算法day06-哈希表篇(2)

荪俗 昨天 18:25
目录


  • 四数相加II
  • 赎金信
  • 三数之和
  • 四数之和
一、四数相加II

454. 四数相加 II - 力扣(LeetCode)
1.png

   主要思路:

  • 分组+哈希表优化

    • 将四个数组分成两组,nums1 和 nums2 为一组,nums3 和 nums4 为另一组。
    • 首先计算 nums1 和 nums2 中所有可能的元素对的和,并用哈希表记录每个和出现的次数。
    • 然后计算 nums3 和 nums4 中所有可能的元素对的和的相反数(即 -(nums3[k] + nums4[l])),并在哈希表中查找该值是否存在。如果存在,说明存在对应的 nums1 + nums2[j] 使得总和为 0,此时将哈希表中的计数累加到结果中。

  • 复杂度分析

    • 时间复杂度:O(N²),其中 N 是数组的长度。我们需要遍历 nums1 和 nums2 的所有组合(O(N²)),以及 nums3 和 nums4 的所有组合(O(N²)),哈希表的插入和查询操作均为 O(1)。
    • 空间复杂度:O(N²),哈希表最多存储 N² 个不同的和。

  1. class Solution {
  2.     public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
  3.         int n = nums1.length;
  4.         Map<Integer, Integer> map = new HashMap<>();
  5.         for(int n1 : nums1){        //先把1和2组中的和存储到map中
  6.             for(int n2 : nums2){
  7.                 int sum = n1+n2;
  8.                 map.put(sum, map.getOrDefault(sum,0)+1);
  9.             }
  10.         }
  11.         int count = 0;
  12.         for(int n1: nums3){         //看3和4组的和是否在map中出现过,出现过几次
  13.             for(int n2: nums4){
  14.                 int target = - n1 - n2;
  15.                 if(map.containsKey(target)){
  16.                     count += map.get(target);
  17.                 }
  18.             }
  19.         }
  20.         return count;
  21.     }
  22. }//时间复杂度:O(N^2)
  23. //空间复杂度:O(N^2)
复制代码
二、赎金信

  383. 赎金信 - 力扣(LeetCode)
2.png

  该代码用于判断 ransomNote 是否能由 magazine 中的字符构成(每个字符只能用一次)。思路是:

  • 统计 magazine 的字符频率(用 HashMap 存储)。
  • 遍历 ransomNote 的字符,检查是否在 magazine 中有足够的字符可用:

    • 如果某个字符不存在或数量不足,返回 false。
    • 否则,减少该字符的剩余可用数量。

  • 如果全部字符都能匹配,返回 true。
  1. class Solution {
  2.     public boolean canConstruct(String ransomNote, String magazine) {
  3.         char[] cc = magazine.toCharArray();
  4.         Map<Character,Integer> map = new HashMap<>();
  5.         for(char c : cc){
  6.             map.put(c, map.getOrDefault(c,0)+1);        //记录每个字符出现的次数
  7.         }
  8.         for(char c : ransomNote.toCharArray()){
  9.             if(!map.containsKey(c)){   
  10.               return false;  
  11.                
  12.             }else{               //若r中出现的字符在m中出现过
  13.                 map.put(c, map.getOrDefault(c,0)-1);        //每出现一次就消耗一次
  14.                 if(map.getOrDefault(c,0)<0){
  15.                     return false;
  16.                 }
  17.             }
  18.             
  19.         }
  20.         return true;
  21.     }
  22. }
  23. //时间复杂度:O(N+M)
  24. //空间复杂度:O(M)(可以优化成O(1))
复制代码
四、四数之和

  地址:https://leetcode.cn/problems/4sum/
3.png

   主要思路:这道题可以用dfs来做,依次对每个数选或不选,就可以选到所有的情况。但这里有一个问题是,选出的path之间不能重复(顺序不同但数字一样也看成重复)。
  1. import java.util.ArrayList;
  2. import java.util.Arrays;
  3. import java.util.List;
  4. class Solution {
  5.     public List<List<Integer>> threeSum(int[] nums) {
  6.         List<List<Integer>> result = new ArrayList<>();
  7.         Arrays.sort(nums); // 排序
  8.         
  9.         for (int i = 0; i < nums.length - 2; i++) {
  10.             if (i > 0 && nums[i] == nums[i - 1]) {
  11.                 continue; // 跳过重复元素
  12.             }
  13.             
  14.             int left = i + 1;
  15.             int right = nums.length - 1;
  16.             
  17.             while (left < right) {
  18.                 int sum = nums[i] + nums[left] + nums[right];
  19.                 if (sum == 0) {
  20.                     result.add(Arrays.asList(nums[i], nums[left], nums[right]));
  21.                     // 跳过重复元素
  22.                     while (left < right && nums[left] == nums[left + 1]) left++;
  23.                     while (left < right && nums[right] == nums[right - 1]) right--;
  24.                     left++;
  25.                     right--;
  26.                 } else if (sum < 0) {
  27.                     left++; // 需要更大的数
  28.                 } else {
  29.                     right--; // 需要更小的数
  30.                 }
  31.             }
  32.         }
  33.         
  34.         return result;
  35.     }
  36. }
复制代码
 
总结

  这篇博客主要是对几道经典的“和类问题”进行梳理,包括四数相加 II、赎金信、三数之和、四数之和。虽然每道题具体做法不同,但其实背后的解题思路是相通的。
像四数相加 II 是典型的“分组 + 哈希优化”,思路比较巧,通过先处理前两个数组的所有组合和,再查找后两个数组对应的反向值,能大大减少时间复杂度。赎金信用的是最基础的哈希计数思想,但也是很多字符串题的基础套路,属于必掌握的部分。
  三数之和和四数之和这两个题就更有代表性了,用排序+双指针或者 DFS+剪枝去重的方式,不仅能解决这类和为目标的组合问题,也能迁移到很多其他类似题上。尤其三数之和是很适合练习双指针写法和去重逻辑的。
  总的来说,刷这几题对我帮助挺大,尤其是在理解“组合类问题怎么拆解”、“怎么控制重复解”、“怎么用哈希表提升效率”等方面,希望这篇总结也能对你有点启发。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册