第454题.四数相加II
思路:比较迷茫,就想着用backtrack
总结:四个数来自四个独立的数组,不用考虑结果重复的问题。所以可以用哈希办法处理。
先用O(n2)的循环把两个数组的结果存入hashmap (空间换时间)
再遍历后面两个数组,找hashmap中是否有对应值 O(n2)
整个算法时间复杂度为O(n2)
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
int res = 0;
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
//统计两个数组中的元素之和,同时统计出现的次数,放入map
for (int i : nums1) {
for (int j : nums2) {
int sum = i + j;
map.put(sum, map.getOrDefault(sum, 0) + 1);
}
}
//统计剩余的两个元素的和,在map中找是否存在相加为0的情况,同时记录次数
for (int i : nums3) {
for (int j : nums4) {
res += map.getOrDefault(0 - i - j, 0);
}
}
return res;
}
}
常用:getOrDefault(key, defaultValue);
383. 赎金信
思路:用hash (数组)来储存第一个字符串的信息,再来检查第二个字符串是否匹配
总结:因为字符串中只有小写字母,只有26种情况,还能用其与字符a的ascii码的差值来代表各个字母,所以可以直接用一个数组来充当哈希
可以在函数开始的时候判断长度大小直接得到false结论
第15题. 三数之和
排序的原因:1. 三数和大于/小于0时候,知道如何操作left&right指针
2. 对与最外层循环的每个i,找到一个答案后,只需要将左右指针同时去重往内部锁一位(去重的情况下),其他答案肯定在内部,不需要重制左右指针
e.g -2,-2,-2,-1,-1,-1,0,0,0,0,0,0,1,1,1,2,2,2,3,3
先发现-2,0,2;再是-1,0,1;最后0,0,0。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new LinkedList<>();
Arrays.sort(nums);
for(int i=0;i<nums.length;i++){
if(nums[i]>0)break;
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[right]+nums[left]>0){
right--;
}else if(nums[i]+nums[right]+nums[left]<0){
left++;
}else{
LinkedList<Integer> temp = new LinkedList<>();
temp.add(nums[i]);
temp.add(nums[left]);
temp.add(nums[right]);
res.add(temp);
while(left<right&&nums[left]==nums[left+1]){
left++;
}
while(left<right&&nums[right]==nums[right-1]){
right--;
}
left++;
right--;
}
}
}
return res;
}
}
a(nums[i])的去重:
nums[i]==nums[i+1] 在进入bc选择前就把所有的重复i给遍历了,去除了b,c与a重复的可能性
应该是i>0&&nums[i]==nums[i-1]来去重,避免这次nums[i]跟之前用的同一个数
b, c的去重 (nums[left], nums[right])
写代码遇到的问题:1.我之前把去重部分放在了if else 中left right更新那里。2.忘记了在找到正确答案后更新left和right并去重,再去寻找下一个答案
1. 只是找到答案,if else的条件就足够让left++, right--了去重写在这里,只是提前去重,没有意义,重要的是在得到答案后,避免答案有重复,所以要对b,c去重
2. 找到答案后,并去重后,记得把left和right都向里移一位,寻找下一个答案,否则会陷入死循环(每个if else的可能性都需要有left,rigth的更新,来达到循环结束的条件)
nums[i]>0的剪枝:在排序的情况下第一个数字大于0,说明sum>0可以直接剪掉
第18题. 四数之和
剪枝:由于target不一定是正数,所以剪枝的条件如下
nums[i]>target && nums[i]>0
首先target或者nums[i]大于零 才能保证nums[i]后面都是正数,所以接着加肯定比target大,可以直接剪枝
去重:j的initial value是i+1,但是去重从i+1之后开始考虑,因为要考虑nums[i]==nums[j]的情况
if (j > i + 1 && nums[j - 1] == nums[j]) { // 对nums[j]去重
continue;
}
nSum: 用递归的方法到2sum 记得去重
public class Solution {
int len = 0;
public List<List<Integer>> fourSum(int[] nums, int target) {
len = nums.length;
Arrays.sort(nums);
return kSum(nums, target, 4, 0);
}
private ArrayList<List<Integer>> kSum(int[] nums, int target, int k, int index) {
ArrayList<List<Integer>> res = new ArrayList<List<Integer>>();
if(index >= len) {
return res;
}
if(k == 2) {
int i = index, j = len - 1;
while(i < j) {
//find a pair
if(target - nums[i] == nums[j]) {
List<Integer> temp = new ArrayList<>();
temp.add(nums[i]);
temp.add(target-nums[i]);
res.add(temp);
//skip duplication
while(i<j && nums[i]==nums[i+1]) i++;
while(i<j && nums[j-1]==nums[j]) j--;
i++;
j--;
//move left bound
} else if (target - nums[i] > nums[j]) {
i++;
//move right bound
} else {
j--;
}
}
} else{
for (int i = index; i < len - k + 1; i++) {
//use current number to reduce ksum into k-1sum
ArrayList<List<Integer>> temp = kSum(nums, target - nums[i], k-1, i+1);
if(temp != null){
//add previous results
for (List<Integer> t : temp) {
t.add(0, nums[i]);
}
res.addAll(temp);
}
while (i < len-1 && nums[i] == nums[i+1]) {
//skip duplicated numbers
i++;
}
}
}
return res;
}
}