454.四数相加II
多个数:分成两组
a + b + c + d = 0 -> a + b = - c - d
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
int n = nums1.length;
int res = 0;
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++){
map.put(nums1[i] + nums2[j], map.getOrDefault(nums1[i] + nums2[j], 0) + 1);
}
}
for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++){
res += map.getOrDefault(- nums3[i] - nums4[j], 0);
}
}
return res;
}
383. 赎金信
哈希表统计existence + frequency
public boolean canConstruct(String ransomNote, String magazine) {
if (ransomNote.length() > magazine.length()) return false;
HashMap<Character, Integer> map = new HashMap<>();
for (int i = 0; i < magazine.length(); i++){
map.put(magazine.charAt(i), map.getOrDefault(magazine.charAt(i), 0) + 1);
}
for (int i = 0; i < ransomNote.length(); i++){
if (!map.containsKey(ransomNote.charAt(i)) || map.get(ransomNote.charAt(i)) <= 0) return false;
map.put(ransomNote.charAt(i), map.get(ransomNote.charAt(i)) - 1);
}
return true;
}
15. 三数之和
需要复习使用指针的去重方法
本解法使用Hash Set进行去重
在去重(更新hi--, lo++)之后仍需要进行一边hi--, lo++,确保对应元素更新到新的数值,避免死循环
public List<List<Integer>> threeSum(int[] nums) {
HashSet<List<Integer>> set = new HashSet<>();
Arrays.sort(nums);
if (nums[0] > 0) return new ArrayList<>(set);
for (int start = 0; start < nums.length - 2; start++){
int lo = start + 1, hi = nums.length - 1;
while(lo < hi){
if (nums[start] + nums[lo] + nums[hi] < 0) lo++;
else if (nums[start] + nums[lo] + nums[hi] > 0) hi--;
else{
set.add(Arrays.asList(nums[start], nums[lo], nums[hi]));
while(lo < hi && nums[lo] == nums[lo + 1]) lo++;
while(lo < hi && nums[hi] == nums[hi - 1]) hi--;
lo++;
hi--;
}
}
}
return new ArrayList<>(set);
}
注:本题也可不使用Hash Set去重,仅需在每层for loop开始时判断start指针新指向的位置和之前是否数值相同
在下一题LC18中也会使用此方法,不使用Hash Set进行去重
18. 四数之和
在读题时需要注意给定数值大小,思考是否存在溢出风险
可参考
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length - 3; i++){
// need nums[i] > 0 to avoid cases such as nums = [-1, -2, -3, -4] && target = -10
if (nums[i] > 0 && nums[i] > target) return res;
// avoid duplicates
if (i > 0 && nums[i] == nums[i - 1]) continue;
for (int j = i + 1; j < nums.length - 2; j++){
// avoid duplicates
if (j > i + 1 && nums[j - 1] == nums[j]) continue;
int lo = j + 1, hi = nums.length - 1;
while(lo < hi){
// use long type because 4 * 10^9 > Integer.MAX_VALUE
long sum = nums[i] + nums[j] + nums[lo] + nums[hi];
if (sum < target) lo++;
else if (sum > target) hi--;
else{
res.add(Arrays.asList(nums[i], nums[j], nums[lo], nums[hi]));
while(lo < hi && nums[lo] == nums[lo + 1]) lo++;
while(lo < hi && nums[hi] == nums[hi - 1]) hi--;
lo++;
hi--;
}
}
}
}
return res;
}
总结:需要多层循环时,可思考是否能使用双指针降低时间复杂度 (reduced by factor n)