454.四数相加II
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
// Map解法
int result = 0;
int temp;
Map<Integer,Integer> map = new HashMap<>();
for(int i : nums1){
for(int j : nums2){
temp = i + j;
if(map.containsKey(temp)){
map.put(temp,map.get(temp)+1);
}else{
map.put(temp,1);
}
}
}
for(int m : nums3){
for(int n : nums4){
temp = m + n;
if(map.containsKey(0-temp)){
result += map.get(0-temp);
}
}
}
return result;
}
}
383. 赎金信
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
// 字符串解法
for(int i = 0;i < ransomNote.length();i++){
String c = String.valueOf(ransomNote.charAt(i));
if(magazine.contains(c)){
magazine = magazine.replaceFirst(c,"");
}else{
return false;
}
}
return true;
}
}
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
// 由题意得,ransomNote为magazine的字符子集
// 数组解法
if(ransomNote.length() > magazine.length()){
return false;
}
int[] hash = new int [26];
for(char m : magazine.toCharArray()){
hash[m - 'a']++;
}
for(char n : ransomNote.toCharArray()){
hash[n - 'a']--;
// 若减到负,则说明用的次数过多,超过子集范围
if(hash[n - 'a'] < 0){
return false;
}
}
return true;
}
}
15. 三数之和
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
// 三指针解法
List<List<Integer>> result = new ArrayList<>();
// 遗漏点
if(nums == null || nums.length < 3){
return result;
}
// 对数组进行排序
Arrays.sort(nums);
// 指针1遍历
for(int i = 0;i < nums.length;i++){
// 剪枝操作1(指针1就>0,说明指针1及之后已无合法序列,
// 全部return结束遍历)
if(nums[i] > 0){
return result;
}
// debug点:对指针1的去重必须比较i和i-1,若比较i和i+1则含义完全不同
// 指针1去重操作(i已经用过,执行去重,结束操作,遍历下一元素)
if(i > 0 && nums[i] == nums[i-1]){
continue;
}
// 指针1处理完成,现在可以固定指针2、指针3
int left = i + 1,right = nums.length - 1;
// left 不能等于 right,否则一共就是两个数了
while(left < right){
// 指针移动处理
if((nums[i] + nums[left] + nums[right]) > 0){
right--;
}else if((nums[i] + nums[left] + nums[right]) < 0){
left++;
}else{
// 收割合法序列
result.add(Arrays.asList(nums[i] , nums[left] , nums[right]));
// 收割合法序列后合法并去重移动left和right
// debug点:对指针2、3的去重必须放在后面,防止在数组元素均相同的情况下跳过合法序列
// 做指针2去重处理(注意使用while而不是if)
while(left < right && nums[left] == nums[left+1]){
left++;
}
// 做指针3去重处理
while(left < right && nums[right] == nums[right-1]){
right--;
}
// left/right持续并去重地向中间移动,直到left >= right
// debug点:对指针2、3的去重必须放在后面,防止在数组元素均相同的情况下跳过合法序列
// 遗漏点
// 找到答案时,双指针同时收缩
right--;
left++;
}
}
}
return result;
}
}
18. 四数之和
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
// 四指针解法
Arrays.sort(nums);
List<List<Integer>> result = new ArrayList<>();
int size = nums.length;
if(size < 4 || nums == null){
return result;
}
// 一重循环指针1
for(int i = 0;i < size;i++){
// 一级去重
if(i > 0 && nums[i-1] == nums[i]){
continue;
}
// 一级剪枝(负数也可能越加越小)
if(nums[i] > target && target > 0 && nums[i] > 0){
return result;
}
// 二重循环指针2
for(int j = i + 1;j < size;j++){
// 二级去重
if(j > i + 1 && nums[j-1] == nums[j]){
continue;
}
// debug点:二级剪枝加上是错的不知道为什么
// // 二级剪枝
// if((nums[i] + nums[j]) > target && target > 0 && (nums[i] + nums[j]) > 0){
// return result;
// }
// 处理指针3、4
// debug点:注意这里与三数之和的区别:这里是和与target比较,三数之和是和与0比较
int left = j + 1,right = nums.length - 1;
while(left < right){
// 在Java中,long和int相加的结果会是long类型。这是因为在Java中,
// 如果一个long类型的数值和一个int类型的数值进行数学运算,
// 结果会自动提升为long类型。这是因为long类型的范围比int类型更大,
// 为了确保不丢失精度,Java会将int类型提升为long类型,然后进行相加。
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]));
// 指针3去重
while(left < right && nums[left] == nums[left+1]){
left++;
}
// 指针4去重
while(left < right && nums[right] == nums[right-1]){
right--;
}
// 捕捉合法数列并去重后,将收缩left和right
left++;
right--;
}
}
}
}
return result;
}
}
tips:三数之和四数之和关键在于去重和剪枝(二级去重和二级剪枝),同时与四数相加||对比考虑。
-
题解2(修正二级剪枝的问题):
-
将二层循环的return result;改为break;
-
解释:假设数组是 1 2 3 4 5 6 , target 是 6,触发剪枝条件的情况可能是 nums[i] = 1, nums[j] = 6,但是第一个数 i 还可以往后选,比如 nums[i] = 2, nums[j] = 3 是满足条件的,所以二层剪枝不能直接return result;需要break;跳出二层循环,回到一层循环继续遍历。
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
// 四指针解法
Arrays.sort(nums);
List<List<Integer>> result = new ArrayList<>();
int size = nums.length;
if(size < 4 || nums == null){
return result;
}
// 一重循环指针1
for(int i = 0;i < size;i++){
// 一级去重
if(i > 0 && nums[i-1] == nums[i]){
continue;
}
// 一级剪枝(负数也可能越加越小)
if(nums[i] > target && target > 0 && nums[i] > 0){
return result;
}
// 二重循环指针2
for(int j = i + 1;j < size;j++){
// 二级去重
if(j > i + 1 && nums[j-1] == nums[j]){
continue;
}
// 二级剪枝
//debug点:修正二级剪枝的问题:
// 将二层循环的return result;改为break;
// 解释:假设数组是 1 2 3 4 5 6 , target 是 6,触发剪枝条件的情况可能是 nums[i] = 1, nums[j] = 6,
// 但是第一个数 i 还可以往后选,比如 nums[i] = 2, nums[j] = 3 是满足条件的,
// 所以二层剪枝不能直接return result;需要break;跳出二层循环,回到一层循环继续遍历。
if((nums[i] + nums[j]) > target && target > 0 && (nums[i] + nums[j]) > 0){
break;
}
// 处理指针3、4
// debug点:注意这里与三数之和的区别:这里是和与target比较,三数之和是和与0比较
int left = j + 1,right = nums.length - 1;
while(left < right){
// 在Java中,long和int相加的结果会是long类型。这是因为在Java中,
// 如果一个long类型的数值和一个int类型的数值进行数学运算,
// 结果会自动提升为long类型。这是因为long类型的范围比int类型更大,
// 为了确保不丢失精度,Java会将int类型提升为long类型,然后进行相加。
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]));
// 指针3去重
while(left < right && nums[left] == nums[left+1]){
left++;
}
// 指针4去重
while(left < right && nums[right] == nums[right-1]){
right--;
}
// 捕捉合法数列并去重后,将收缩left和right
left++;
right--;
}
}
}
}
return result;
}
}