力扣算法:454.四数相加|| 383.赎金信 15.三数之和 18.四数之和

学习内容

力扣算法:

454.四数相加||

383.赎金信

15.三数之和

18.四数之和

具体内容

454.四数相加||

给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:

0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l]  == 0

示例 1:

输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2] 输出:2
解释: 两个元组如下:

  1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
  2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0

示例 2:

输入:nums1 = [0], nums2 = [0], nums3 = [0], nums4 = [0] 输出:1

提示:

n == nums1.length
n == nums2.length
n == nums3.length
n == nums4.length
1 <= n <= 200
-228 <= nums1[i], nums2[i], nums3[i], nums4[i] <= 228

454.四数相加->题目地址

做题思路

由于求得的元组是可重复的,因此思路与“两数之和”类似,先用hashmap获取前两个数组不同位置分别相加得到的值temp,再用0-temp判断后面两个数组不同位置分别相加是否有相对应的值;且由于返回元组个数,因此可以将前两个数组的值temp当作key,而相加得到的值temp在数组的次数当作value

解题

class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
    	HashMap<Integer,Integer> map = new HashMap<>();
    	for(int i:nums1){
    		for(int j:nums2){
    			int temp = i+j;
    			if(map.containsKey(temp)){
    				map.put(temp,map.get(temp)+1);
    			}else{
    				map.put(temp,1);
    			}
    		}
    	}
    	//该变量返回的元组个数
    	int res = 0;
    	for(int i:nums3){
    		for(int j:nums4){
    			int temp = i+j;
    			if(map.containsKey(0-temp)){
    				res += map.get(0-temp);
    			}
    		}
    	}
    	return res;
    }
 }   

解答代码存在的疑问

1、为什么不是直接返回map的key值对应的value,而是需要把满足的条件的value相加
因为有4个数相加,他的排列方式肯定不值一种,而我们需要把满足条件的每一种出现的次数进行相加,最终才能得到所有的元素

383.赎金信

给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。

如果可以,返回 true ;否则返回 false 。

magazine 中的每个字符只能在 ransomNote 中使用一次。

示例 1:

输入:ransomNote = “a”, magazine = “b” 输出:false

示例 2:

输入:ransomNote = “aa”, magazine = “ab” 输出:false

示例 3:

输入:ransomNote = “aa”, magazine = “aab” 输出:true

提示:

1 <= ransomNote.length, magazine.length <= 105
ransomNote 和 magazine 由小写英文字母组成

383.赎金信->题目地址

做题思路

此题类似于“有效的字母异位词”,且有题目可得,ransomNote长度比magazine小且由magazine的元素组成,仍然可采用ASC||26为编码的思路,判断ransomNote每个字符出现的次数的是否小于magazine每个字符的次数

解题

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        int res[] = new int[26];
        for(int i = 0;i < magazine.length();i++){
        	res[magazine.charAt(i)-'a']++;
	    }
	    for(int j = 0;j < ransomNote.length();j++){
	    	res[ransomNote.charAt(j)-'a']--;
	    }
	    for(int i:res){
			if(i < 0){
				return false;
			}
	    }
	    return true;
	}
}	
解答代码存在的疑问

1、为什么当存放的数组元素比0时,说明ransomNote有字符是没有在magazine里面?
一开始元素都为0,后面对magazine进行操作,则对于其字符都会+1,而后对ransomNote进行操作,对ransomNote出现的字符则进行-1,因此要是字符在ransomNote出现而magezine不出现则数组里的对应的元素会变成比0小的数

15.三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例 1:

输入:nums = [-1,0,1,2,-1,-4] 输出:[[-1,-1,2],[-1,0,1]] 解释: nums[0] +
nums[1] + nums[2] = (-1) + 0 + 1 = 0 。 nums[1] + nums[2] + nums[4] = 0

  • 1 + (-1) = 0 。 nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。 不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。 注意,输出的顺序和三元组的顺序并不重要。

示例 2:

输入:nums = [0,1,1] 输出:[] 解释:唯一可能的三元组和不为 0 。

示例 3:

输入:nums = [0,0,0] 输出:[[0,0,0]] 解释:唯一可能的三元组和为 0 。

提示:

3 <= nums.length <= 3000
-105 <= nums[i] <= 105

15.三数之和 -> 题目地址

做题思路

1、一开始想的是用哈希解法,两层for循环就可以确定 a 和b 的数值了,可以使用哈希法来确定 0-(a+b) 是否在数组里出现过,但题目要求不可以包含重复的三元组,而若是把符合条件的三元组放进数组中在进行去重,则会过于复杂
2、使用双指针,这样就有利于我们不会取到重复的数值

解题

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> list = new ArrayList<List<Integer>>();
        //使用双指针必须是有序的
        Arrays.sort(nums);
        //将i值看作第一个数,left和right分别看出第二个和第三个
        for(int i = 0;i < nums.length;i++){
        	//剪枝:由于前面已排好序,因此若是第一个元素大于0,后面的元素就都大于0
        	if(nums[i] > 0){
        		return list;
        	}
        	//去重,当前值与上一个值比较,i>0
        	if(i > 0 && nums[i] == nums[i-1]){
        		continue;
        	}
        	int left = i+1;
        	int right = nums.length-1;
        	//不能是left <= right的原因,若是相等,则元素重复,不符合题意
        	while(left<right){
        		if(nums[i] + nums[left] + nums[right] == 0){
        			list.add(Arrays.asList(nums[i],nums[left],nums[right]));
        			//去重
        			while(right > left && nums[left] == nums[left+1]){
        				left++;
        			}
        			while(right > left && nums[right] == nums[right-1]){
        				right--;
        			}
        			right--;
        			left++;
        		}else if(nums[i] + nums[left] + nums[right] > 0){
        			right--;
        		}else{
        			left++;
        		}
        	}
       }
       return list;
   }
}

解答代码存在的疑问

1、对第一个元素第一次去重的理解:
对第一个元素第一次去重的理解

2、对第二、三个元素第二次去重的理解:
对第二、三个元素第二次去重

18.四数之和

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]](若两个四元组元素一一对应,则认为两个四元组重复):

0 <= a, b, c, d < n
a、b、c 和 d 互不相同
nums[a] + nums[b] + nums[c] + nums[d] == target

你可以按 任意顺序 返回答案 。

示例 1:

输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

示例 2:

输入:nums = [2,2,2,2,2], target = 8 输出:[[2,2,2,2]]

提示:

1 <= nums.length <= 200
-109 <= nums[i] <= 109
-109 <= target <= 109

18.四数之和 -> 题目地址

做题思路

与上述三数之和解法一致,但要注意是四个数字,有两次循环,以及题目给定了target值,因此剪枝的处理和上述不同

解题

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
    	ArrayList<List<Integer>> list = new ArrayList<>();
    	Arrays.sort(list);
    	for(int i = 0;i < nums.length;i++){
    		//剪枝处理:由于是给定的target,并不知晓是负数还是正数,因此要判断第一个元素大于0而直接返回链表,则要保证第一个元素在大于0的前提下还要大于target
    		if(nums[i] > 0 && nums[i] > target) {
    			return list;
    		}
    		//去重
    		if(i > 0 && nums[i] == nums[i-1]){
    			continue;
    		}
    		for(int j = i+1; j < nums.length;j++){
    			//去重
    			if(j > i+1 && nums[j] == nums[j-1]){
    				continue;
    			}
    			int left = j+1;
    			int right = nums.length - 1;
    			while(right>left){
                    if(nums[i]+nums[j]+nums[left]+nums[right] == target){
                        list.add(Arrays.asList(nums[i],nums[j],nums[left],nums[right]));
                        while(left < right && nums[left] == nums[left+1]){
                            left++;
                        }
                        while(left < right && nums[left] == nums[left+1]){
                            right--;
                        }
                        left++;
                        right--;
                    }else if(nums[i]+nums[j]+nums[left]+nums[right] < target){
                        left++;
                    }else if(nums[i]+nums[j]+nums[left]+nums[right] > target){
                        right--;
                    }
                }
            }
        }
        return list;
    }
 }

解答代码存在的疑问

1、剪枝为什么要存在nums[i] > 0 的情况
这保证整条链表都是正数,且第一个就可以与target进行比较,若存在负数,这无法比较

复习

454.四数相加||

class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        int result = 0
        HashMap<Integer,Integer> map = new HashMap<>();
        for(int i = 0;i < nums1.length;i++){
            for(int j = 0; j < nums2.length;j++){
                int temp = nums1[i] + nums2[j];
                if(map.containsKey(temp)){
                    map.put(temp,map.get(temp)+1);
                }
                map.put(temp,1);
            }
        }
        for(int k = 0;k < nums3.length;k++){
            for(int l = 0;l < nums4.length;l++){
                int cur = nums3[k] + nums4[l];
                if(map.containsKey(0-cur)){
                    result += map.get(0-cur);
                }
            }
        }
        return result;
    }
}

383.赎金信

按字母异位词的思路解题

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        int result[] = new int[26];
        for(int i = 0;i < ransomNote.length();i++){
            result[ransomNote.charAt(i) - 'a']++;
        }
        for(int j = 0;j < magazine.length();j++){
            result[magazine.charAt(j) - 'a']--;
        }
        for(int a : result){
            if(a > 0){
                return false;
            }
        }
        return true;
    }
}

15.三数之和

考虑用set 但要求返回list

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Set<Integer> set = new Set<Integer>();
        List<Set<Integer>> list = new ArrayList<>();
        HashMap<Integer,Integer> map =new HashMap<>();
        for(int k = 0; k < nums.length;k++){
            map.put(nums[k],k);
        }
        for(int i:nums){
            for(int j:nums){
                int temp = i + j;
                if(map.containsKey(0-temp)){
                    set.add(i);
                    set.add(j);
                    set.add(0-temp);
                    list.add(set);
                }
            }
        }
    }
}

矫正

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> list = new ArrayList<List<Integer>>();
        //使用双指针必须是有序的
        Arrays.sort(nums);
        //将i值看作第一个数,left和right分别看出第二个和第三个
        for(int i = 0;i < nums.length;i++){
        	//剪枝:由于前面已排好序,因此若是第一个元素大于0,后面的元素就都大于0
        	if(nums[i] > 0){
        		return list;
        	}
        	//去重,当前值与上一个值比较,i>0
        	if(i > 0 && nums[i] == nums[i-1]){
        		continue;
        	}
        	int left = i+1;
        	int right = nums.length-1;
        	//不能是left <= right的原因,若是相等,则元素重复,不符合题意
        	while(left<right){
        		if(nums[i] + nums[left] + nums[right] == 0){
        			list.add(Arrays.asList(nums[i],nums[left],nums[right]));
        			//去重
        			while(right > left && nums[left] == nums[left+1]){
        				left++;
        			}
        			while(right > left && nums[right] == nums[right-1]){
        				right--;
        			}
        			right--;
        			left++;
        		}else if(nums[i] + nums[left] + nums[right] > 0){
        			right--;
        		}else{
        			left++;
        		}
        	}
       }
       return list;
   }
}

18.四数之和

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
    	ArrayList<List<Integer>> list = new ArrayList<>();
    	Arrays.sort(list);
    	for(int i = 0;i < nums.length;i++){
    		//剪枝处理:由于是给定的target,并不知晓是负数还是正数,因此要判断第一个元素大于0而直接返回链表,则要保证第一个元素在大于0的前提下还要大于target
    		if(nums[i] > 0 && nums[i] > target) {
    			return list;
    		}
    		//去重
    		if(i > 0 && nums[i] == nums[i-1]){
    			continue;
    		}
    		for(int j = i+1; j < nums.length;j++){
    			//去重
    			if(j > i+1 && nums[j] == nums[j-1]){
    				continue;
    			}
    			int left = j+1;
    			int right = nums.length - 1;
    			while(right>left){
                    if(nums[i]+nums[j]+nums[left]+nums[right] == target){
                        list.add(Arrays.asList(nums[i],nums[j],nums[left],nums[right]));
                        while(left < right && nums[left] == nums[left+1]){
                            left++;
                        }
                        while(left < right && nums[left] == nums[left+1]){
                            right--;
                        }
                        left++;
                        right--;
                    }else if(nums[i]+nums[j]+nums[left]+nums[right] < target){
                        left++;
                    }else if(nums[i]+nums[j]+nums[left]+nums[right] > target){
                        right--;
                    }
                }
            }
        }
        return list;
    }
 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值