LeetCode 454.四数相加II
题目链接:454.四数相加II
思路:四个数组中找四个数使得四个数的和为0,并返回所有组合的个数。由于这边要返回的是所有组合个数,即要判断和是否等于0的同时也要保存所有和为0这个组合的个数,类似于定义count=0,每找到一个这样的组合count++这样的效果。其实这可以用map键值对来进行处理。key是算出来的和。value是所有和为0组合的个数。
具体实现:
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
Map<Integer,Integer> map = new HashMap<>();
int res=0;
for(int i=0;i<nums1.length;i++){
for (int j=0;j<nums2.length;j++){
int temp = nums1[i]+nums2[j];
if(map.containsKey(temp)){
map.put(temp,map.get(temp)+1);
}else{
map.put(temp,1);
}
}
}
for(int k=0;k<nums3.length;k++){
for (int l=0;l<nums4.length;l++){
int temp = nums3[k]+nums4[l];
if(map.containsKey(0-temp)){
res += map.get(0 - temp);
}
}
}
return res;
}
图解:
细节处理:
对于四个数组才有两两相加可以将时间复杂度降到最低
对于返回值res不是得到一个符合条件的组合就++这么简单
数组中的如果遇到相同的和,则代表组合个数+1所以要把原先的和对应的value取出来+1再放进去
在遍历3、4两个数组时候,算出和temp,判断0-temp是否在该map中。如果再相当于多出一个组合这在四个组合其实是相乘的一个结果。所以这边要不断累加
LeetCode 383. 赎金信
题目链接:383. 赎金信
思路:昨天第一道的变种题。magazine字符串中的字母能否构成ransomNote字符串中的字母。magazine中字母不能重复使用。依旧是先让magazine映射到26个字母,出现一次就++。然后ransomNote去找对应的--。之后如果26个字母中有小于0的。代表ransomNote中有magazine中不存在的。也就表示magazine字符串中的字母不能否构成ransomNote字符串中的字母。
具体实现:
public boolean canConstruct(String ransomNote, String magazine) {
int[] nums =new int[26];
for (int i=0;i<magazine.length();i++){
nums[magazine.charAt(i)-'a']++;
}
for (int i=0;i<ransomNote.length();i++){
nums[ransomNote.charAt(i)-'a']--;
if(nums[ransomNote.charAt(i)-'a']<0){
return false;
}
}
return true;
}
图解:思路想到了图解其实跟昨天第一道差不多,可以看昨天的图解
LeetCode 15. 三数之和、 LeetCode 18. 四数之和
题目链接:15. 三数之和
题目链接:18. 四数之和
思路:这两题很类似,所以放在这里一起说了。给定一个数组,找出三个和为0。返回所有可能的结果,作为一个二维数组。给定一个数组,找出四个和为目标值。返回所有可能的结果,作为一个二维数组。有重复的组合算作一个。即不能有重复的。用数组的思想来解决这道题。对于三个数。定义i先指向第一个元素。定义left和right分别指向i后面一个元素和最后一个元素。然后遍历i时候让left--,right++找出所有符合的组合。这种方法比三重for降低了不少的时间。四个数即在i前边再加一个k指针,让i指向k+1,left指向i+1,right还是指向最后一个元素即可。重点思路在于去重。
具体实现:
//三数之和
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
int left = i+1;
int right = nums.length-1;
if(nums[i]>0){
return result;
}
if(i>0&&nums[i]==nums[i-1]){
continue;
}
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]));
while (left<right && nums[left]==nums[left+1])
left++;
while (left<right && nums[right]==nums[right-1])
right--;
right--;
left++;
}
}
}
return result;
}
//四数之和
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
for (int i = 0; i <nums.length ; i++) {
if (nums[i] > 0 && nums[i] > target) {
return result;
}
if(i>0&&nums[i-1]==nums[i]){
continue;
}
for (int j = i+1; j < nums.length; j++) {
if(j>i+1&&nums[j]==nums[j-1]){
continue;
}
int left = j+1;
int right = nums.length-1;
while (left<right){
if(nums[i]+nums[j]+nums[left]+nums[right] > target){
right--;
} else if (nums[i]+nums[j]+nums[left]+nums[right] < target) {
left++;
}else {
result.add(Arrays.asList(nums[i],nums[j], nums[left], nums[right]));
while (left<right && nums[left]==nums[left+1])
left++;
while (left<right && nums[right]==nums[right-1])
right--;
right--;
left++;
}
}
}
}
return result;
}
图解:
细节处理
难点在于去重。对于k,i,left,right都要去重,核心思想是如果当前值与前一个相同则就往前进即可
即对于最外层的k如果nums[k]==nums[k-1]则continue即跳出循环进入新的一轮循环,也就是k往前进
还有一个难点是减枝。说实话,三数之和不减枝也可过,四数之和为了让你减枝特意搞了一个测试用例。你不减就过不了如下图当然为了通过不用减枝用其他方法也可以把这个测试用例解决掉
由于是排序过的。减枝就是不用进入循环,nums[i] > 0 && nums[i] > target的时候直接跳出循环即可