454. 又是抄袭别人思路的一天,分组➕哈希表,妙啊。思路就是把4个数组分成2个数组,然后就是2个数组的和相加看看有没有等于目标值的出现。这里不是用的相加,用的是求差,很常见的思维了算是,不知道的可以去看上一篇 两数之和 用哈希表去感受下。
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
// key为两个数字所有可能性的和,value这个”和“出现的频次!
Map<Integer, Integer> map = new HashMap<>();
for(int i : nums1){
for(int j : nums2){
int sum1 = i+j;
map.put(sum1, map.getOrDefault(sum1,0)+1);
}
}
//统计可能性
int count=0;
for(int j: nums3){
for(int k: nums4){
int sum2 = j+k;
// 这里很重要,(a+b) + (c+d) = 0 -> sum1 + sum2 = 0 -> sum1 = 0-sum2;
//这里就是要找到前面两个数字在的和(sum1)在map中存在的次数!!!(求差)
count += map.getOrDefault(0-sum2, 0);
}
}
return count;
}
383. 这道题目之前,建议可以先做下 242.有效的字母异位词 ,或者直接看我之前的文章也有写过这道题目。这次我上来又是想的直接用map,但是看了提示数组+哈希表其实更好,map的消耗太大了!(切记切记,在哈希表的题目中不要只想着map这些一定要想到还有数组真的效率很高!!!)。这里跟242. 的思路一样的就不多说了,直接看代码注释。
public boolean canConstruct(String ransomNote, String magazine) {
int[] arry = new int[26];
// magazine的每个字符出现的次数统计放在arry中
// 这里题目说了全是小写的字母哦
for(int i =0; i<magazine.length(); i++){
char tmp1 = magazine.charAt(i);
arry[tmp1 - 'a']++;
}
// 统计ransomNote中每个字符出现的次数,没出现一次arry对应的位置就减去1
for(int j=0; j<ransomNote.length(); j++){
char tmp2 = ransomNote.charAt(j);
arry[tmp2 - 'a']--;
}
// 如果n小于零,说明ransomNote中出现的次在magazine中没出现过
// 又或者出现过一次但是ransomNote中出现过两次也是不行的
// 因为同一个不能重复多次使用
for(int n : arry){
if(n<0) return false;
}
return true;
}
15. 做的迷迷糊糊的这道题目,好晕。一半抄抄写写一般思考写出来的,不是特别特别理解下次估计还是不会。比较重要的几个点:
- 排序。
- 双指针,i != j、i != k 且 j != k, 这个条件已经通过双指针隐性的实现了每个下表不会相同满足题目需求。
- 对排序后的数组去重。
- 对
nums[i]
的去重: 在外层循环中,当i
位置的元素和前一个位置i-1
的元素相等时,就继续前进到下一个位置。这是因为数组已经排序,所以相同的元素会连续出现。如果不跳过这些重复的元素,就会对于相同的nums[i]
重复寻找nums[left]
和nums[right]
,从而生成重复的三元组。 - 找到和为0的三元组后,对
nums[left]
和nums[right]
的去重: 在找到一个有效的三元组之后,即nums[i] + nums[left] + nums[right] == 0
,需要将left
指针向右移动跳过所有相同的元素,同样right
指针向左移动跳过所有相同的元素。这是因为在nums[i]
固定的情况下,我们已经找到了与之配对的一个唯一的nums[left]
和nums[right]
组合,接下来需要寻找的是新的nums[left]
和nums[right]
组合,因此需要跳过所有重复的值以防止生成重复的三元组。
- 对
下面这个指针图片来自Carl哥的讲解:
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
// a = nums[i], b = nums[left], c = nums[right]
// i != j、i != k 且 j != k, 这个已经通过双指针隐性的实现了每个下表不会相同
for(int i=0; i<nums.length; i++){
// 如果排序后最小的值已经大于零,就不能有=0的和存在了
if(nums[i] > 0) return res;
// 对a进行去重, 去重就是为了避免重复的三元数组!
if(i>0 && nums[i] == nums[i-1]) continue;
//left是中间指针,在i和right之间移动所以初始在i的后一个位置
int left = i+1;
int right = nums.length-1;
while(left < right){
int sum = nums[i] + nums[left] + nums[right];
if(sum > 0){
right--;
}else if(sum < 0){
//这里是left动不是i动!
left++;
}else{//这里就是找到符合条件的三个数了
// list操作一次性加入多个数,list里面存list,这个操作可以记住!
res.add(Arrays.asList(nums[i],nums[left],nums[right]));
// 去重逻辑应该放在找到一个三元组之后,对b 和 c去重
while(left<right && nums[left] == nums[left+1]) left++;
while(left<right && nums[right] == nums[right-1]) right--;
left++;
right--;
}
}
}
return res;
}
18. 这道题目跟上面的代码几乎一摸一样就是在while循环外又多了一层for环外,两个for循环一个while循环,您直接循环死我得了😭。我还是写认真的理解好上面的那题然后就可以闭着眼这题了哈哈哈(假的。附上讲解链接,但是还是得看懂上面那题讲解才能看懂这一题是真话。