class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
int result = 0;
Map<Integer, Integer> map = new HashMap<>();
for(int i: nums1){
for(int j: nums2){
int sum = i+j;
map.put(sum, map.getOrDefault(sum, 0)+1);
}
}
for(int i: nums3){
for(int j: nums4){
result = result + map.getOrDefault(0-i-j,0); // 0-(c+d)=a+b
}
}
return result;
}
}
思路:
-
首先,使用一个哈希表
map
来存储数组nums1
和nums2
中所有可能的两两之和以及对应的频率。这是通过双重循环实现的。 -
在填充哈希表的过程中,对于每个
i
(来自nums1
)和j
(来自nums2
)的组合,我们计算它们的和,并使用map.put(sum, map.getOrDefault(sum, 0) + 1);
来更新哈希表中的频率。 -
在第一步之后,已经得到了
nums1
和nums2
中所有可能的两两之和及其对应的频率。 -
再次使用双重循环遍历数组
nums3
和nums4
中的所有元素组合。对于每个i
(来自nums3
)和j
(来自nums4
)的组合,我们检查-i-j
在哈希表中的频率(因为我们希望找到 a+b+c+d = 0 的组合,所以 a+b = -c-d)。我们将此频率添加到结果result
中。 -
注意,这里的
i
和j
实际上分别对应于c
和d
-
最后,返回结果
result
,它表示满足条件的元素组合的数量。
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
// shortcut
if (ransomNote.length() > magazine.length()) {
return false;
}
// 定义一个哈希映射数组
int[] record = new int[26];
// 遍历
for(char c : magazine.toCharArray()){
record[c - 'a'] += 1;
}
for(char c : ransomNote.toCharArray()){
record[c - 'a'] -= 1;
}
// 如果数组中存在负数,说明ransomNote字符串总存在magazine中没有的字符
for(int i : record){
if(i < 0){
return false;
}
}
return true;
}
}
思路:
-
首先,检查
ransomNote
的长度是否大于magazine
。如果是,我们可以立即返回false
,因为一个较长的字符串不可能由一个较短的字符串构成。 -
定义一个
record
数组来存储26个小写字母的计数。该数组的大小是固定的,为26,对应26个小写字母。初始化所有值为0。 -
之后,我们遍历
magazine
中的所有字符,并更新它们在record
数组中的计数。 -
接下来,我们遍历
ransomNote
中的所有字符,并减去相应的计数。 -
最后,我们检查
record
数组,看看是否有任何负值。如果存在负值,那意味着ransomNote
中有一个字符出现的次数超过了它在magazine
中的次数。在这种情况下,返回false
。如果所有值都是非负的,那么返回true
。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>(); //定义一个结果二维数组
Arrays.sort(nums); //降序排序
// 找出a + b + c = 0
// a = nums[i], b = nums[left], c = nums[right]
for(int i = 0; i < nums.length; i++){
if(nums[i]>0){
return result;
}
if(i > 0 && nums[i-1] == nums[i]){ //去重a
continue;
}
int left = i+1;
int right = nums.length - 1;
while(right > left){
int sum = nums[i] + nums[left] + nums[right];
if(sum < 0){
left++;
}else if(sum > 0){
right--;
}else{
result.add(Arrays.asList(nums[i],nums[left], nums[right]));
// 去重逻辑应该放在找到一个三元组之后,对b 和 c去重
while(right>left && nums[right]==nums[right-1]){
right--;
}
while(right>left && nums[left]==nums[left+1]){
left++;
}
}
left++;
right--;
}
}
return result;
}
}
思路:
-
初始化:创建一个结果列表来存储所有的三数组合。
-
排序:排序数组可以简化问题,因为当数组是排序的,我们可以简单地移动指针来增加或减少三个数字的和。
-
遍历数组:固定数字
a
,并使用双指针找到b
和c
。- 如果
a
大于0,则break。因为三个正数的和不可能为0。 - 如果当前数字和前一个数字相同(即重复),则跳过,以避免重复组合。
- 如果
-
双指针:对于每个固定的
a
,使用双指针技术查找与其匹配的b
和c
。left
指针指向a
之后的元素,right
指针指向数组的最后一个元素。- 如果
a + b + c < 0
,则left
指针向右移动。 - 如果
a + b + c > 0
,则right
指针向左移动。 - 如果
a + b + c == 0
,我们找到了一个解,将其添加到结果列表中。然后移动指针并跳过所有重复的b
和c
值。
-
返回结果:返回存储所有三数组合的结果列表。
-
对b和c进行去重,注意要先收集完结果再去重
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
// nums[i] > target 直接返回, 剪枝操作
if (nums[i] > 0 && nums[i] > target) {
return result;
}
if (i > 0 && nums[i - 1] == nums[i]) { // 对nums[i]去重
continue;
}
for (int j = i + 1; j < nums.length; j++) {
if (j > i + 1 && nums[j - 1] == nums[j]) { // 对nums[j]去重
continue;
}
int left = j + 1;
int right = nums.length - 1;
while (right > left) {
// nums[k] + nums[i] + nums[left] + nums[right] > target int会溢出
long sum = (long) nums[i] + nums[j] + nums[left] + nums[right];
if (sum > target) {
right--;
} else if (sum < target) {
left++;
} else {
result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
// 对nums[left]和nums[right]去重
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
left++;
right--;
}
}
}
}
return result;
}
}
思路:
-
排序数组: 首先对数组进行排序。排序的主要目的有两个:一是方便后面的去重操作;二是为了使用双指针技术。
-
三重循环 + 双指针:
-
外层循环 (
i
):遍历整个数组,寻找第一个数字。对于nums[i]
,有两个关键的剪枝操作:一是当nums[i]
大于target
且为正数时,直接返回结果,因为后续的数字都会更大,不可能存在合适的四元组;二是为了去重,如果当前的nums[i]
和前一个数字相同,则跳过此次循环。 -
第二层循环 (
j
):在i
之后,遍历数组寻找第二个数字。和外层循环类似,这里也有去重的操作,即如果nums[j]
和前一个数字相同,则跳过此次循环。 -
双指针:固定了前两个数字后,使用双指针技术寻找后两个数字。定义
left
和right
两个指针,left
指向j
的下一个位置,right
指向数组的最后一个位置。然后根据四数之和与target
的大小关系来移动指针,具体地:如果和小于target
,则增加left
;如果和大于target
,则减小right
;如果和等于target
,则将这四个数字加入结果中,并且移动指针到下一个不重复的位置。
-
-
返回结果: 遍历完数组后,返回找到的所有四元组。