454. 四数相加 II
给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:
0 <= i, j, k, l < nnums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
思路-使用map
假设我们有四个数组 A B C D, 遵循以下步骤:
- 定义一个map集合,key 放 a + b 的和,value 放这个和出现的次数
- 遍历 A 数组和 B 数组,统计两个数组中数字两两相加的和,以及出现的次数,放入map中
- 定义一个变量
count用来统计a + b + c + d = 0的次数。 - 遍历 C 数组和 D 数组,然后寻找
key = 0 - c - d是否存在于map中,如果存在,count += map.get(key);否则就继续循环。 - 最后返回
count
注意: 是
count += map.get(key)而不是count++;
因为假设a + b = 5出现了2次,而c + d = -5出现了1次。那么这样应该有2种情况a + b + c + d = 0。
例如:{a = 5, b = 0, c = -5, d = 0} {a = 2, b = 3, c = -5, d = 0};
代码实现
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
int count = 0;
Map <Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums1.length; ++i) {
for (int j = 0; j < nums2.length; ++j) {
int key = nums1[i] + nums2[j];
map.put(key, map.getOrDefault(key, 0) + 1);
}
}
for (int i = 0; i < nums3.length; ++i) {
for (int j = 0; j < nums4.length; ++j) {
int value = nums3[i] + nums4[j];
int key = 0 - value;
count += map.getOrDefault(key, 0);
}
}
return count;
}
}
- 时间复杂度:O(n^2)
- 空间复杂度: O(n^2),最坏情况下A和B的值各不相同,相加产生的数字个数为 n^2
383. 赎金信
给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。
如果可以,返回 true ;否则返回 false 。
magazine 中的每个字符只能在 ransomNote 中使用一次。
思路
和有效字母异位一个解法,只不过在第二循环判断条件的时候变成一旦dict数组中有任一个数小于0,那么ransomNote 就不能由 magazine 里面的字符构成。如果没有任何数小于0,则可以由magazine 里面的字符构成。
代码实现
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
int[] dict = new int[26];
for (int i = 0; i < magazine.length(); ++i ) {
dict[magazine.charAt(i) - 'a']++;
}
for (int i = 0; i < ransomNote.length(); ++i) {
if (--dict[ransomNote.charAt(i) - 'a'] < 0) {
return false;
}
}
return true;
}
}
- 时间复杂度:O(n)
- 空间复杂度:O(1)
15. 三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
你返回所有和为 0 且不重复的三元组。
注意: 答案中不可以包含重复的三元组。
思路-双指针法
**思路参考下图: **
先排序
然后我们通过一个 for 循环用来先固定一个数,i 从下标 0 开始, left = i + 1, right = nums.length - 1;
然后计算 nums[i] + nums[left] + nums[right] 是否等于0,如果等于0,就找到了一个结果集,如果大于0,我们就应该right--,因为我们对数组排过序,因此如果结果大于0,那么就应该减小nums[right],如果小于0,就left++。
重复这个流程直到 i 遍历完整个数组。
去重操作
如果 nums[i] == nums[i - 1], 那么就代表以 nums[i] 为第一个数的结果集已经被收割过了,因此不用再检查了,可以直接跳过此次循环;
同时,如果 nums[left] == nums[left - 1]; nums[right] == nums[right + 1] 也是同理,如果遇到这两种情况也直接left++; right--;
代码实现
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
int left;
int right;
List<List<Integer>> list = new ArrayList<>();
for (int i = 0; i < nums.length; ++i) {
// 减枝操作,因为是从小到大排序的,因此如果第一个数已经大于0了
// 那么不管怎么加,不可能等于0。
if (nums[i] > 0) {
continue;
}
// 去重操作
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
left = i + 1;
right = nums.length - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum > 0) {
right--;
} else if (sum < 0) {
left++;
} else {
list.add(Arrays.asList(nums[i], nums[left], nums[right]));
left++;
right--;
// 去重操作
while ( left < right && nums[left] == nums[left - 1]) {
left++;
}
while (left < right && nums[right] == nums[right + 1]) {
right--;
}
}
}
}
return list;
}
}
- 时间复杂度: O(n^2)
- 空间复杂度: O(1)
18. 四数之和
给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d < na、b、c和d互不相同nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。
思路-双指针
和 三数之和 思路一样,只不过多了一层for循环,具体参考思路 三数之和
代码实现
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
Arrays.sort(nums);
List<List<Integer>> list = new ArrayList<>();
for (int i = 0; i < nums.length; ++i) {
// 剪枝:非必要操作,但可以优化效率
if (nums[i] > 0 && nums[i] > target) {
continue;
}
// 去重
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
for (int j = i + 1; j < nums.length; ++j) {
// 二级剪枝:非必要操作,但可以优化效率
if (nums[i] + nums[j] > target && nums[j] + nums[i] >= 0) {
break;
}
// 去重
if (j > (i + 1) && nums[j] == nums[j - 1]) {
continue;
}
int left = j + 1;
int right = nums.length - 1;
while (left < right) {
long sum = (long)nums[i] + nums[j] + nums[left] + nums[right];
if (sum < target) {
left++;
} else if ( sum > target) {
right--;
} else {
list.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
left++;
right--;
// 去重
while (left < right && nums[left] == nums[left - 1]) {
left++;
}
while (left < right && nums[right] == nums[right + 1]) {
right--;
}
}
}
}
}
return list;
}
}
- 时间复杂度: O(n^3)
- 空间复杂度: O(1)