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
思路
先同时遍历nums1和nums2数组,将两数组的元素依次相加,统计和的频数,存储在哈希表map中。
要令nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0,则需要nums1[i] + nums2[j] == - (ums3[k] + nums4[l])
同时遍历nums3和nums4数组,判断- (ums3[k] + nums4[l])是否在map中,如果在,则当前满足条件的情况有- (ums3[k] + nums4[l])的频数种。
代码
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
Map<Integer,Integer> map=new HashMap<>();
for(int i=0;i<nums1.length;i++){
for(int j=0;j<nums2.length;j++){
map.put(nums1[i]+nums2[j],map.getOrDefault(nums1[i]+nums2[j],0)+1);
}
}
int count=0;
for(int i=0;i<nums3.length;i++){
for(int j=0;j<nums4.length;j++){
if(map.containsKey(-(nums3[i]+nums4[j]))){
count+=map.get(-(nums3[i]+nums4[j]));
}
}
}
return count;
}
}
383. 赎金信
给你两个字符串:
ransomNote和magazine,判断ransomNote能不能由magazine里面的字符构成。 如果可以,返回true;否则返回false。magazine中的每个字符只能在ransomNote中使用一次。
思路
统计ransomNote中各字母出现的频次,再遍历magazine,当出现ransomNote中的字母时,该字母对应的频数减1。在遍历完成后,如果有字母的频数仍大于1,则ransomNote 不能由 magazine 里面的字符构成。
代码
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
int[] map=new int[26];
for(int i=0;i<ransomNote.length();i++){
map[ransomNote.charAt(i)-'a']++;
}
for(int i=0;i<magazine.length();i++){
map[magazine.charAt(i)-'a']=map[magazine.charAt(i)-'a']>0?map[magazine.charAt(i)-'a']-1:0;
}
for(int i=0;i<map.length;i++){
if(map[i]>0){
return false;
}
}
return true;
}
}
也可以在遍历magazine时进行判断,代码如下:
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
int[] map=new int[26];
for(int i=0;i<magazine.length();i++){
map[magazine.charAt(i)-'a']+=1;
}
for(int i=0;i<ransomNote.length();i++){
map[ransomNote.charAt(i)-'a']-=1;
if(map[ransomNote.charAt(i)-'a']<0){
return false;
}
}
return true;
}
}
15. 三数之和
给你一个整数数组
nums,判断是否存在三元组[nums[i], nums[j], nums[k]]满足i != j、i != k且j != k,同时还满足nums[i] + nums[j] + nums[k] == 0。请 你返回所有和为0且不重复的三元组。 注意: 答案中不可以包含重复的三元组。
思路
使用双指针法。
- 对给定数组进行自然排序(从小到大)。
- 对数组进行遍历,对每一个数组元素,固定其作为三元组的一个元素,使用
start和end将该元素后的数组序列从两端进行遍历,判断nums[i] + nums[start] + nums[end]与0的关系,当等于0时,找到一个三元组;否则,调整start或end的位置继续遍历。
Tips
- 遍历数组,每个元素作为三元组中的固定元素。
for(int i=0;i<nums.length-2;i++)中i的取值:在nums[i]后的数组序列中还要取两个值组成三元组,也就是nums[i]后至少还有 2 个元素,i最大取值为i-3。while(start<end)中 为什么不是start<=end:因为{ nums[i], nums[start], nums[end] }三元组中,nums[start]和nums[end]不能是同一个数组元素。
代码
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res=new ArrayList<>();
// 从小到大排序
Arrays.sort(nums);
int start,end,curNum;
// nums[i]后至少还应有2个元素
// i的最大值为 nums.length-3
for(int i=0;i<nums.length-2;i++){
if(nums[i]>0){
break;
}
if(i>0&&nums[i-1]==nums[i]){
continue;
}
start=i+1;
end=nums.length-1;
// start不能等于end
// nums[i] nums[start] nums[end]构成一个三元组
while(start<end){
curNum=nums[i]+nums[start]+nums[end];
if(curNum==0){
// 找到
res.add(Arrays.stream(new Integer[]{nums[i],nums[start],nums[end]}).toList());
// 去重
// 跳过与nums[start]和nums[end]相同的元素
do{
start++;
}while(start<end&&nums[start-1]==nums[start]);
do{
end--;
}while(start<end&&nums[end+1]==nums[end]);
}else if(curNum>0){
end--;
}else{
start++;
}
}
}
return res;
}
}
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
你可以按 任意顺序 返回答案 。
思路
与15.三数之和使用的方法一样,先固定四元组的前两个元素,再通过双指针遍历后面数组序列来寻找使条件成立的数组元素。
Tips
- 使用两个
for循环(fori和forj)来确定四元组中的前两个元素,使用start和end来遍历四元组第二个元素后的数组序列。因此,四元组的表示为:{ nums[i], nums[j], nums[start], nums[end] } - 见 15.三数之和分析,本题中
i的最大值为nums.length-4,j的最大值为nums.length-3。 nums[i] > target && (nums[i] > 0 || target >= 0)剪枝条件:- 已知
nums[i+1]到nums[nums.length-1]都大于(或等于)nums[i] - 在满足
nums[i] > target条件时, - 如果
target >= 0,nums[i]与后面的元素的和必然大于等于nums[i],则必然大于target,条件必然不成立,可以剪枝。
- 当
target < 0,- 如果
nums[i] < 0,数组中下标i之后的元素有可能是负数,nums[i]加上一个负数,结果小于nums[i],即结果有可能是target,条件有可能成立,不可以剪枝。 - 如果
nums[i] > 0,nums[i]加上任何正数一定大于0,则必然大于target,条件不成立,可以剪枝。
- 如果
- 已知
代码
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> res=new ArrayList<>();
Arrays.sort(nums);
int start,end,curNum;
// nums[i]后至少有 3 个元素
for(int i=0;i<nums.length-3;i++){
if(i>0&&nums[i-1]==nums[i]){
continue;
}
// nums[j]后至少有 2 个元素
for(int j=i+1;j<nums.length-2;j++){
if(nums[i]>target&&(nums[i]>0||target>=0)){
break;
}
if(j>i+1&&nums[j-1]==nums[j]){
continue;
}
start=j+1;
end=nums.length-1;
while(start<end){
curNum=nums[i]+nums[j]+nums[start]+nums[end];
if(curNum==target){
// 找到四元组
res.add(Arrays.stream(new Integer[]{nums[i],nums[j],nums[start],nums[end]}).toList());
// 去重
do{
start++;
}while(start<end&&nums[start-1]==nums[start]);
do{
end--;
}while(start<end&&nums[end+1]==nums[end]);
}else if(curNum>target){
end--;
}else{
start++;
}
}
}
}
return res;
}
}