JAVA二刷-Day7 | 哈希表 454.四数相加II, 383. 赎金信, 15. 三数之和, 18. 四数之和
四数相加 II
LeetCode题目链接:https://leetcode.cn/problems/4sum-ii/
解题思路
四数相加可以看做是两数相加的一个升级版,将其抽象为 ( a + b ) + ( c + d ) (a+b)+(c+d) (a+b)+(c+d),然后像两数相加一样处理即可。
但值得注意的一点是,四数相加中 ( a + b ) (a+b) (a+b)可能有多个组合,因此需要进行计数处理。在后续计数时加上之前的组合数。
代码如下:
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
Map<Integer, Integer> map1 = new HashMap<>();
int result = 0;
for (int a: nums1) {
for (int b: nums2) {
int sum = a + b;
if (map1.containsKey(sum)) {
int val = map1.get(sum);
map1.put(sum, ++val);
}else {
map1.put(sum, 1);
}
}
}
for (int c: nums3) {
for (int d:nums4) {
int sum = -(c + d);
if (map1.containsKey(sum)) {
int val = map1.get(sum);
result += val;
}
}
}
return result;
}
}
赎金信
LeetCode题目链接:https://leetcode.cn/problems/intersection-of-two-arrays/
解题思路
计算交集同理,构造出一个数组的哈希表,用另一个数组进行查找,如果查询值大于0则说明属于交集元素。
特别注意,因为要求交集元素每个出现一次,所以在查询到一次后,该处元素应该减少1。
使用java的时候,因为是连续的字母作为map进行搜索所以其实不用使用hashmap结构,增加冗余的搜索,直接使用char[]数组即可。
map版本代码如下:
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
Map<Character, Integer> charMap = new HashMap<>();
char[] magArr = magazine.toCharArray();
char[] ranArr = ransomNote.toCharArray();
for (int i = 0; i < magArr.length; i++) {
charMap.put(magArr[i], charMap.getOrDefault(magArr[i], 0) + 1);
}
for (int j = 0; j < ranArr.length; j++) {
int val = charMap.getOrDefault(ranArr[j], 0);
if (val != 0) {
charMap.put(ranArr[j], --val);
}else {
return false;
}
}
return true;
}
}
数组形式版本代码如下(偷懒抽的力扣代码:P):
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
if (ransomNote.length() > magazine.length()) {
return false;
}
int[] cnt = new int[26];
for (char c : magazine.toCharArray()) {
cnt[c - 'a']++;
}
for (char c : ransomNote.toCharArray()) {
cnt[c - 'a']--;
if(cnt[c - 'a'] < 0) {
return false;
}
}
return true;
}
}
三数之和
LeetCode题目链接:https://leetcode.cn/problems/3sum/
解题思路
由于每个数字都不可重复,因此多重循环是不现实的。可以引入多指针来分别指向不同的位置,以此来避免重复值的出现。同时注意避免组合相同的情况,应该对数组中的元素迭代时进行去重。
进行去重的时候不仅是第一个数字需要去重,其他的两个数字在使用后也应当进行去重。
具体代码如下:
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
if (i > 0 && nums[i - 1] == nums[i]) {
continue;
}
int left = i + 1, right = nums.length - 1;
while (left < right) {
if (nums[left] + nums[right] == -nums[i]) {
result.add(new ArrayList<Integer>(Arrays.asList(nums[i], nums[left], nums[right])));
}else if (nums[left] + nums[right] > -nums[i]){
right--;
continue;
}else {
left++;
continue;
}
while (left < right && nums[left] == nums[left + 1]) {
left++;
}
while (left < right && nums[right] == nums[right - 1]) {
right--;
}
left++;
right--;
}
}
return result;
}
}
四数之和
LeetCode题目链接:https://leetcode.cn/problems/4sum
解题思路
先看题目,与三数之和其实相当于多了一个值,因为需要后侧最后两个值收缩来完成最后的判定。因此新的值迭代应该放在前侧的索引附近,并且满足去重的要求。
关于去重不涉及第一个数字的理解:
k+1不循环是对数值的保护。在特殊情况,比如数组所有元素值相同的时候,第一个元素不进行去重是为了能将四个数值都在数组中的位置初始化出来。就像第一层循环要k>0在进行后面判断一样,一方面是为了可以查询到k-1,另一方面也保证就算所有元素相同,代码也可以初始化出四个数的位置,以此跑一次完整的流程。如果一上来i>k条件被满足了,那么在后面元素相同的情况下第二个数就直接跑到数组末尾了,后面第三个数和第四个数根本没有被设置位置和进行迭代。
代码如下:
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
int i, j, k, l;
for (i = 0; i < nums.length; i++) {
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
for (j = i + 1; j < nums.length; j++) {
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
k = j + 1;
l = nums.length - 1;
while (k < l) {
long sum = (long)nums[i] + nums[j] + nums[k] + nums[l];
if (sum == target) {
result.add(Arrays.asList(nums[i], nums[j], nums[k], nums[l]));
}else if (sum < target) {
k++;
continue;
}else if (sum > target) {
l--;
continue;
}
while (k < l && nums[k] == nums[k + 1]) {
k++;
}
while (k < l && nums[l] == nums[l - 1]) {
l--;
}
k++;
l--;
}
}
}
return result;
}
}