454.四数相加Ⅱ
题目链接/文章讲解/视频讲解:https://programmercarl.com/0454.%E5%9B%9B%E6%95%B0%E7%9B%B8%E5%8A%A0II.html
分析题目:从4个数组中各找一个元素相加等于0,相同元素可以重复使用
思路:我们要查找元素并且求和,那么我们就要考虑到哈希法,哈希法在本题中要如何使用,首先我们应该遍历前两个集合,找出其中所有nums1[i]+nums2[j]存入map集合中,key为和,val为和出现的次数,然后再遍历后两个集合,同样相加,再去集合中查找另外一半是否在集合中
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
Map<Integer,Integer> map = new HashMap<>();
// 遍历前两个集合
for(int i:nums1){
for(int j: nums2){
// 每个组合进行求和
int sum = i+j;
// 以(sum,sum出现的次数)存入map中,这里不需要去重
// 一个数组中的一个元素可以使用多次,例如示例1中的nums3[0]
// getOrDefault(键,默认值):存在键则取出里面的值,不存在则使用默认值
map.put(sum,map.getOrDefault(sum,0)+1);
}
}
int count=0;
// 遍历后两个集合
for(int i:nums3){
for(int j:nums4){
// 每个组合进行求和
int sum = i+j;
int num = 0-sum;
// 根据计算的结果在map中查找,找到了则count进行计数
if(map.containsKey(num)){
count = map.get(num)+count;
}
}
}
return count;
}
383.赎金信
题目链接/文章讲解:https://programmercarl.com/0383.%E8%B5%8E%E9%87%91%E4%BF%A1.html
思路:这题的做法与有效字母异位词做法差不多,这里就不详细解释,代码中有注释
public boolean canConstruct(String ransomNote, String magazine) {
if(ransomNote.length() > magazine.length()) return false;
// 遍历ransomNote字符串,并存储到数组中
int[] strArr = new int[26];
for(int i=0; i<ransomNote.length(); i++){
strArr[ransomNote.charAt(i)-97]++;
}
// 遍历ransomNote字符串,减去数组中存储的内容
for(int i=0; i<magazine.length(); i++){
strArr[magazine.charAt(i)-97]--;
}
// 只要数组中还存在字符串ransomNote的其他字符(大于0的值),则返回false
for(int i:strArr){
if(i > 0) {
return false;
}
}
return true;
}
15.三数之和
题目链接/文章讲解/视频讲解:https://programmercarl.com/0015.%E4%B8%89%E6%95%B0%E4%B9%8B%E5%92%8C.html
题目分析:在一个数组中找出3个不同下标的元素相加和要等于0,不同的下标,那么我们就要提前考虑去重的问题
思路:这题可以使用双指针的做法,使用一个for循环来遍历数组,left指针指向i+1的位置,right指向数组最后一个元素,然后内部使用while循环,来根据3个下标中的值判断当前结果是否满足要求
去重逻辑:结合代码中的注释一起看
为什么代码中的 nums[i]==nums[i-1] 是i-1而不是i+1,举个例子[-1,-1,2],如果说当前i指向第一个-1时,如果条件为nums[i]==nums[i+1]就会忽略掉这个正确的结果集,后续left和right去重逻辑与注释中类似
注意:去重是去掉重复的三元组,而不是数组中重复的元素
例如:[ [1,2,-3], [2,-3,1] ],这种才是重复,而不是[-1,-1,2]内部元素重复
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
// 首先对数组进行排序
Arrays.sort(nums);
for(int i=0; i<nums.length; i++){
if(nums[i]>0) return result;
// nums[i]==nums[i-1]:对i进行去重,因为我们已经提前对数组进行了排序,
// 如果当前元素等于我们前一个元素,那么就表示以这个元素开头的所有组合的结果我们已经全部收集了
// 因为三元组不能重复,所以可以直接跳过这个元素,i>0是防止第一个元素索引越界
if(i>0 && nums[i]==nums[i-1]) continue;
int left = i+1;
int right = nums.length-1;
while(left < right){
// 和大于0,我们就要缩短right,因为数组是排过序的
if(nums[i]+nums[left]+nums[right] > 0){
right--;
}else if(nums[i]+nums[left]+nums[right] < 0){ // 同理
left++;
}else{
result.add(Arrays.asList(nums[i],nums[left],nums[right]));
// 必须在收集结果后,进行去重判断
// 对left,right进行去重,逻辑同上,注意还是因为数组是有序的
while(nums[right]==nums[right-1] && right>left) right--;
// 如果left这里的元素一直重复,那么我们直接取最后一个left就可以了
while(nums[left]==nums[left+1] && right>left) left++;
// 当我们收集到一次结果后,left和right同时移动
right--;
left++;
}
}
}
return result;
}
18.四数之和
题目链接/文章讲解/视频讲解:https://programmercarl.com/0018.%E5%9B%9B%E6%95%B0%E4%B9%8B%E5%92%8C.html
思路:与三数之和思路相同,只不过内部使用两个for进行遍历,唯一的区别就是三数之和要求是等于0,而四数之和是等于target,所以一些条件判断需要调整,大家就直接看代码和注释吧
List<List<Integer>> result = new ArrayList<>();
// 跟三数之和同样的思路,唯一需要注意的点就是对target值得条件判断
// 因为三数之和,求得是和等于0,而四数之和求得是和等于target
Arrays.sort(nums);
for(int i=0; i<nums.length; i++){
// 如果第一个元素的值大于target那么就直接返回,前提是当前元素必须要大于0
if(nums[i]>target && nums[i]>0) return result;
// 对i去重
if(i>0 && nums[i]==nums[i-1]) continue;
for(int j=i+1; j<nums.length; j++){
// 对j去重
if(j>i+1 && nums[j]==nums[j-1]) continue;
int left = j+1;
int right = nums.length-1;
while(left < right){
// 注意:这里的sum是和target做比较,后面的思路与三数之和一样
if(nums[i]+nums[j]+nums[left]+nums[right] > target){
right--;
}else if(nums[i]+nums[j]+nums[left]+nums[right] < target){
left++;
}else{
result.add(Arrays.asList(nums[i],nums[j],nums[left],nums[right]));
while(nums[right]==nums[right-1] && right>left) right--;
while(nums[left]==nums[left+1] && right>left) left++;
left++;
right--;
}
}
}
}
return result;
}