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

LeetCode 454.四数相加II

题目链接:454.四数相加II

解题思路

分析题目可知:

  1. 这道题需要快速在数组中查找出满足题目条件的所有可能,我们需要使用哈希表。
  2. 计算有多少个元组 (i, j, k, l) ,我们不需要输出具体的位置
  3. nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0,我们将这个条件转换为nums1[i] + nums2[j] == -( nums3[k] + nums4[l])。

思路一(哈希表的应用—map)

我可以将nums1[i] + nums2[j] 和nums3[k] + nums4[l]分开进行遍历,将nums1[i] + nums2[j] 放进map(key = nums1[i] + nums2[j] ,value = 出现的次数),然后遍历-(nums3[k] + nums4[l]),再在map中查找,在对查找出来的次数进行累加。
在这里插入图片描述

class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        int sum = 0;
        Map<Integer, Integer> map1 = new HashMap<Integer, Integer>();
        Map<Integer, Integer> map2 = new HashMap<Integer, Integer>();
        for(int i = 0; i < nums1.length; i++){
            for(int j = 0; j < nums2.length; j++){
                map1.put(nums1[i] + nums2[j], map1.getOrDefault(nums1[i] + nums2[j], 0) + 1);
                
            }
        } 
        for(int i = 0; i < nums3.length; i++){
            for(int j = 0; j < nums4.length; j++){
                if(map1.containsKey(-(nums3[i] + nums4[j]))){
                    sum = sum + map1.get(-(nums3[i] + nums4[j]));
                }
            }
        }
        return sum;
    }
}

LeetCode 383. 赎金信

题目链接:383. 赎金信

解题思路

分析题目:

  1. 这道题需要快速找到每个字母进行比较,我需要使用哈希表来解题
  2. ransomNote能不能由magazine里面的字符构成,则ransomNote的字母组成都是有magazine组成。
  3. magazine 中的每个字符只能在 ransomNote 中使用一次,我们需要考虑字母出现的次数,选择哈希表的数据结构:set集合无法使用。
  4. ransomNote和 magazine由小写英文字母组成,我们将ransomNote和magazine转换为char,因为小写字母ASCII是连续的,长度固定为26,可以使用数组。

思路一(哈希表的应用—map)

我们使用map作为我们的哈希表的数据结构,我们由题意可知ransomNote必须全由magazine的字母组成,所以将ransomNote存入map(字母,出现的次数),然后遍历magazine遍历对map进行查询如果存在相同字母就将该字母的次数减一,如果该字母的次数小于零或不存在该字母都不符合题意,输出false。

在这里插入图片描述

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        if(ransomNote.length() > magazine.length()){
            return false;
        }
        Map<Character, Integer> map = new HashMap<Character, Integer>();
        for(int i = 0; i < magazine.length(); i++){
            char c = magazine.charAt(i);
            map.put(c, map.getOrDefault(c, 0) + 1);
        }
        for(int i = 0; i < ransomNote.length(); i++){
            char s = ransomNote.charAt(i);
            if(!map.containsKey(s)||map.get(s) == 0){
                return false;
            }
            map.put(s, map.get(s) - 1);
        }
        return true;
    }
}

思路二(哈希表的应用—数组)

与思路一不同,使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!因为两个字符串都是由小写字母构成,我们可以设置一个长度为26的数组,每个索引代表一个字母(通过ASCII码实现),其他步骤和map的解题思路后面大致一样。

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

LeetCode 15. 三数之和

题目链接:15. 三数之和

解题思路

思路一(双指针法)

  1. 我们首先应该将nums进行排序。
  2. a:我们将第一个遍历作为a的选取,但是我们需要对a进行去重,a[i] != a[i - 1],前提时i > 1。
    b和c:我们设置两个指针分别为left,right,left = a + 1, right = nums.length - 1,然后让两个指针进行移动,分以下三种情况: 如果nums[i] + nums[left] + nums[right] > 0,我们将right往左移,如果nums[i] + nums[left] + nums[right] < 0,让left往右移,如果等于则存入list,我们还需要对b和c去重,我们在对进行移动时对left和right进行判断如果nums[right] == nums[right - 1],就让right一值移动到不是重复值的后面,left也同理进行去重。

步骤如下图:
在这里插入图片描述 在这里插入图片描述
在这里插入图片描述

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

思路二(哈希表的应用—set)(不推荐)

在这里插入图片描述
上图展示了解题思路

  1. 如果开头大于0,a就会往左移,如果a与a的前一个值相等,一直移动到与前一个值不相等的地方开始遍历b
  2. 如果set中没有-(a+b)则将b放入到set集合中,如果由就将abc移入list中。
  3. b移动的规则,如果移动到b > a + 2的位置时我要考虑b前两个位置是不是都与自己相等,如果相等就一直移动到与前两个值有至少有一个不相等的地方(-1 ,0,1,1,2)
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> list = new ArrayList<>();
        Arrays.sort(nums);
        for(int i = 0; i < nums.length; i++){
            if(nums[i] > 0){
                return list;
            }
            if(i > 0 && nums[i] == nums[i - 1]){
                continue;
            }
            Set<Integer> set = new HashSet<Integer>();
            for(int j = i + 1; j < nums.length; j++){
                if(j > i + 2 && nums[j] == nums[j - 1] && nums[j - 1] == nums[j - 2]){
                    continue;
                }
                int c = 0 - (nums[i] + nums[j]);
                if(set.contains(c)){
                    list.add(Arrays.asList(nums[i], nums[j], c));
                    set.remove(c);
                }else{
                    set.add(nums[j]);
                }
            }
        }
        return list;
    }
}

LeetCode 18. 四数之和

题目链接:18. 四数之和

解题思路

思路一(双指针法)

思路与LeetCode 15. 三数之和的思路一是一样的,只是在其基础上加入一个for(){}。
需要注意几点:

  1. 这道题是四数相加之和等于target,所以我们不能使用,num[n] > targrt(num[n]代表排序后开头的数),如果target = -4 ,num[] = {-5,-1-1,0,0,1},所以我们只能写当num[n] > target,且num[n] > 0,直接输出list,其他的地方没有什么不太一样的,照葫芦画瓢就可以实现。
class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> list = new ArrayList<>();
        Arrays.sort(nums);
        for(int n = 0; n < nums.length; n++){
            if(nums[n] > target && nums[n] > 0){
                return list;
            }
            if(n > 0 && nums[n] == nums[n - 1]){
                    continue;
            }
            for(int i = n + 1; i < nums.length; i++){
                if(i > n + 1 && nums[i] == nums[i - 1]){
                    continue;
                }
                int left = i + 1;
                int right = nums.length - 1;
                while(left < right){
                    if(nums[i] + nums[left] + nums[right] + nums[n] > target){
                        right--;
                    }else if(nums[i] + nums[left] + nums[right]  + nums[n] < target){
                        left++;
                    }else{
                        list.add(Arrays.asList(nums[i], nums[left], nums[right], nums[n]));
                        while(right > left && nums[right] == nums[right - 1]){
                        right--;
                        }
                        while(right > left && nums[left] == nums[left + 1]){
                            left++;
                        }
                        right--;
                        left++;
                    }  
                }
            }
        }
        return list;
    }
}
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值