454.四数相加II 【来自四个数组的值相加】
- 自我尝试
- 思路: 因为题上要求返回int,即符合要求的元组数量。因此可以采用HashMap方法。具体思路为通过一个HashMap将前两个数组的和记录下来:key为和,value为出现次数。随后利用二数之和类似方法将剩下两个数组再次进行遍历,即检验(0-c-d)是否为HashMap里的一个key,是的话就说明我们找到了个符合要求的元组!累加下来,得出并返回最后的结果。
- 代码:O(n^2)
class Solution { public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) { // 1. 定义map,key为nums1,nums2的和,value为出现次数 HashMap<Integer,Integer> hm = new HashMap<>(); // 2. 遍历nums1, nums2来完善map for(int ele1:nums1){ for(int ele2:nums2){ int tmp = ele1+ele2; if(hm.containsKey(tmp)){ hm.put(tmp,hm.get(tmp)+1); }else{ hm.put(tmp,1); } } } // 3. 声明一个int count,来返回结果 int count = 0; // 4. 遍历nums3, nums4, 使用(-nums3[i]-nums4[j]==map.get(key)?); 累加count的结果 for(int ele3:nums3){ for(int ele4:nums4){ if(hm.containsKey(-ele3-ele4)){ count+=hm.get(-ele3-ele4); } } } // 5. 返回 return count; } }
- 思路: 因为题上要求返回int,即符合要求的元组数量。因此可以采用HashMap方法。具体思路为通过一个HashMap将前两个数组的和记录下来:key为和,value为出现次数。随后利用二数之和类似方法将剩下两个数组再次进行遍历,即检验(0-c-d)是否为HashMap里的一个key,是的话就说明我们找到了个符合要求的元组!累加下来,得出并返回最后的结果。
- 范例尝试
- 思路: 与上相同
- 代码
class Solution { public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) { // 1. 定义map,key为nums1,nums2的和,value为出现次数 HashMap<Integer,Integer> hm = new HashMap<>(); // 2. 遍历nums1, nums2来完善map for(int ele1:nums1){ for(int ele2:nums2){ int tmp = ele1+ele2; // 如果存在返回,不存在就返回0 hm.put(tmp,hm.getOrDefault(tmp,0)+1); } } // 3. 声明一个int count,来返回结果 int count = 0; // 4. 遍历nums3, nums4, 使用(-nums3[i]-nums4[j]==map.get(key)?); 累加count的结果 for(int ele3:nums3){ for(int ele4:nums4){ count+=hm.getOrDefault(-ele3-ele4,0); } } // 5. 返回 return count; } }
- 收获
- Hashmap的方法: getOrDefault(key, defaultVal)。 这个方法首先尝试get某个key,如果key不存在于map,那就返回一个默认值。通过这样的方式,可以省去个if-else判断。
383. 赎金信
- 自我尝试
- 思路: 与字母异同位思路类似。看一个string A,是否能由string B里的char所拼出来,核心就是统计stirng A char数量后,在遍历string B char做差,如果有大于0的余下来,则表明无法完全由string B 拼接出来。
- 代码
class Solution { public boolean canConstruct(String ransomNote, String magazine) { int[] count = new int[26]; for(int i=0; i<ransomNote.length(); i++){ count[ransomNote.charAt(i)-'a']++; } for(int j=0;j<magazine.length();j++){ count[magazine.charAt(j)-'a']--; } for(int ele:count){ if(ele>0){ return false; } } return true; } }
- 收获
- 再次强调: string的方法为charAt(index)而非charsAt(index)
15. 三数之和 【同一个数组内的三数之和】
- 自我尝试
- 思路
- 暴力法求解去重很麻烦
- 思路
- 范例尝试
- 思路:双指针法!
- 整体:首先以增序排列数组。随后通过两个指针(left,right)加一个loop base,在loop循环迭代中,俩指针sum与base的和与0相比做出偏移。即 若和大于0,需要减少sum,right指针向左偏移。同理,若和小于0,left指针向右偏移。特殊情况出现在当三数和恰好等于0的时候。添加这三个数,随即左右偏移进行去重处理!
- headsup1: base需要去重!不然会出现重复结果。base去重逻辑为当前是否与前面一个数相同。是nums[i] == nums[i-1],而非nums[i] == nums[i + 1]。原因为如果后者的话会跳过当前的数据pass
- headsup2:再temp>0, temp<0时候没必要去重,因为仍会自动偏移。
- headsup3:当三数和恰好为0时候,左右偏移去重后,记得确保左右的值与先前已不同。
- 整体:首先以增序排列数组。随后通过两个指针(left,right)加一个loop base,在loop循环迭代中,俩指针sum与base的和与0相比做出偏移。即 若和大于0,需要减少sum,right指针向左偏移。同理,若和小于0,left指针向右偏移。特殊情况出现在当三数和恰好等于0的时候。添加这三个数,随即左右偏移进行去重处理!
- 代码
class Solution { public List<List<Integer>> threeSum(int[] nums) { // 1. 声明返回的数组,并排序数组 List<List<Integer>> result = new ArrayList<>(); Arrays.sort(nums); // 2. 从base:nums[0]开始遍历,声明两个指针:left,right。当base与left,right和小于0,left右移,right向左移,如果相等后,加入返回的数组,同时left与right都要去重在偏移前。 for(int i=0;i<nums.length-2;i++){ // 2.0 break 条件 if(nums[i]>0){ break; } // 2.1 base去重 if(i>0 && nums[i]==nums[i-1]){ continue; } // 2.2 body int left = i+1; int right = nums.length -1; while(left<right){ int temp = nums[i]+nums[left]+nums[right]; if(temp<0){ //说明left需要递增 left++; }else if(temp>0){ right--; }else{ result.add(Arrays.asList(nums[i],nums[left],nums[right])); //2.3 做去重处理 while(left<right && nums[left]==nums[left+1]) left++; while(left<right && nums[right]==nums[right-1]) right--; //2.4 这俩仍要做!不然的话如果有重复的,以重复的值出现 left++; right--; } } } // 2.3 返回 return result; } }
- 收获
- array方法:
- Arrays.sort(arr)这是给数组升序排序。
- Arrays.asList(val1,val2...); 这是一个生成arraylist的方法 ;
- Note1:Arrays.asList() 返回的是一个实现List接口的对象,但其实际上为一个特殊的ArrayList实例。
- array方法:
- 思路:双指针法!
- 范例尝试
- 思路: 与三数之和类似。外面再套一层循环达到O(n^3)的复杂度。但注意相应的去重步骤略有改动。
- Headsup1: 注意溢出的问题。
- 代码
class Solution { public List<List<Integer>> fourSum(int[] nums, int target) { //双指针法 Arrays.sort(nums); List<List<Integer>> result = new ArrayList<>(); for(int i =0; i<nums.length-3; i++){ if (nums[i] > 0 && nums[i] > target) { return result; } if(i>0 && nums[i]==nums[i-1]){ continue; } for(int j=i+1; j<nums.length-2;j++){ // i+1 每次也要 if(j>i+1 && nums[j]==nums[j-1]){ continue; } int left = j+1; int right = nums.length-1; while(left<right){ int temp = nums[i]+nums[j]+nums[left]+nums[right]; if(temp<target){ left++; }else if(temp>target){ right--; }else{ result.add(Arrays.asList(nums[i],nums[j],nums[left],nums[right])); while(left<right && nums[left]==nums[left+1]) left++; while(left<right && nums[right]==nums[right-1]) right--; left++; right--; } } } } return result; } }
- 思路: 与三数之和类似。外面再套一层循环达到O(n^3)的复杂度。但注意相应的去重步骤略有改动。
总结
- 三数之和与四数之和都是同一数组内求出不重复的元素和为target。双指针法要求它们先进行排序;而这点与使用HashMap的二数之和方法不同,因为二数之和要求返回元素下标。如果二数之和返回的是值,也可以采用双指针法。
- 四数相加是有序且可重复,因而用HashMap来解决,降为O(n^2)