目录
今日学习的文章
四数相加II
建议:本题是 使用map 巧妙解决的问题,好好体会一下 哈希法 如何提高程序执行效率,降低时间复杂度,当然使用哈希法 会提高空间复杂度,但一般来说我们都是舍空间 换时间, 工业开发也是这样。
题目链接/文章讲解/视频讲解:代码随想录
看到题目的第一想法
其实没什么思路,想着用哈希,审题出错,计算有多少个元组,我以为是两数相加那种,要返回下表
当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。
其实和两数之和的做法和这个思路比较雷同,
看到代码随想录之后的想法
用一个双重for循环存入nums1 和nums2 的和,到map的key中,value为出现的次数
再用一个for循环num3 num4 get(0-(nums1+nums2)),获得出现多少次
自己实现过程中遇到的困难
/*class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
//n为长度
//有四个数组用哈希?
//当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法
//数组的值加起来
//我审题有问题,题目要求是计算有多少个元组,而我想的是要把每个元组都要查出来
//卡哥的视频思路,:先处理前面两个数组,将用map集合将nums1和nums2 的所有得到的和当成key存起来,
//而value存放的是,在前两个数组中有多少个组合能组成所得到的和,存放的是这个和出现的次数
//而在遍历nums3和nums4时,利用0-(nums3+nums4)得到的值,作为key去map中查找有没有符合的,
//如果有则得到相关的value(次数)
Map<Integer,Integer> map = new HashMap<Integer,Integer>();
for(int i=0;i<nums1.length;i++){
for(int j=0;j<nums2.length;j++){
if(map.containsKey(nums1[i]+nums2[j])){
int num = map.get(nums1[i]+nums2[j]);
num++;
map.put(nums1[i]+nums2[j],num);
}else{
//这里应该为1 写的时候为0
map.put(nums1[i]+nums2[j],1);
}
}
}
int count = 0;
for(int i=0;i<nums3.length;i++){
for(int j=0;j<nums4.length;j++){
int offset = 0-(nums3[i]+nums4[j]);
if(map.containsKey(offset)){
count+=map.get(offset);
}
}
}
return count;
}
}*/
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
//n为长度
//有四个数组用哈希?
//当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法
//数组的值加起来
//我审题有问题,题目要求是计算有多少个元组,而我想的是要把每个元组都要查出来
//卡哥的视频思路,:先处理前面两个数组,将用map集合将nums1和nums2 的所有得到的和当成key存起来,
//而value存放的是,在前两个数组中有多少个组合能组成所得到的和,存放的是这个和出现的次数
//而在遍历nums3和nums4时,利用0-(nums3+nums4)得到的值,作为key去map中查找有没有符合的,
//如果有则得到相关的value(次数)
Map<Integer,Integer> map = new HashMap<Integer,Integer>();
//map.getOrDefault方法获取指定 key 对应的 value,如果找不到 key ,则返回设置的默认值。
for(int i :nums1){
for(int j:nums2){
int sum = i+j;
map.put(sum,map.getOrDefault(sum,0)+1);
}
}
int count = 0;
for(int i :nums3){
for(int j:nums4){
count+=map.getOrDefault(0-(i+j),0);
}
}
return count;
}
}
看到题目的第一想法
和242一样的逻辑,用一个26位的数组来判断每个字符出现了多少次
但是也有不同,它是判断一个单词,能不能由另一个单词组成,所以数组中存储长单词,短单词的时候减减,结束时判断是否出现负数
当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。
看到代码随想录之后的想法
自己实现过程中遇到的困难
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
//判断magazine中的字符是否在ransomNote中出现
//字符异位是怎么做的?用一个数组
//用一个int数组将26为字符一一对应
//记录每个字符出现的次数,如果最后都为0则返回true,反之为false
int[] note = new int[26];
for(int i=0;i<magazine.length();i++){
note[magazine.charAt(i)-'a']++;
}
for(int i=0;i<ransomNote.length();i++){
note[ransomNote.charAt(i)-'a']--;
}
//还是有区别,这个题只是判断A是否在B中出现,我先讲数量大的数据存入数组,然后再判断数量小的数组是否在数组中
//其实在本题的情况下,使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!
for(int i=0;i<note.length;i++){
if(note[i]<0){
return false;
}
}
return true;
}
}
三数之和
建议:本题虽然和 两数之和 很像,也能用哈希法,但用哈希法会很麻烦,双指针法才是正解,可以先看视频理解一下 双指针法的思路,文章中讲解的,没问题 哈希法很麻烦。
题目链接/文章讲解/视频讲解:代码随想录
看到题目的第一想法
用哈希,先存入map再循环
当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。
看到代码随想录之后的想法
双指针,细节比较多
1 先排序
双指针,一个指针指向i,另外设置left和right指针,left指向i的后一个指针,right指向数组的最后位置来
进行相加操作来分别判断
其中题干要求返回三元组,但是数值不能重复(返回的是数值数组不是下标)
需要考虑去重问题,当i与后面的相等,重复值不用处理了,往后continue,当left和right处理完后,left++,right--,
直到不相等为止
其中i的处理需要额外考虑相同值的问题,返回的三元组是会出现相同的值的[nums[i],nums[j],nums[k]]
因为要允许结果出现 nums[i]==nums[j],所以不能i与后面的相等直接就continue,需要考虑结果有相同值i>0的问题,
先要让i处理了一次,所以要i>0后再continue
自己实现过程中遇到的困难
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
//三个不同的下标,同时满足nums[i]+nums[j]+nums[k]==0
//哈希做法,先存入map再双重循环?
//卡哥做法,用双指针来实现,先对数组排序
//1双指针,一个指针指向i,另外设置left和right指针,left指向i的后一个指针,right指向数组的最后位置
//来进行相加操作来分别判断
//其中题干要求返回三元组,但是数值不能重复(返回的是数值数组不是下标)
//需要考虑去重问题,当i与后面的相等,重复值不用处理了,往后continue,当left和right处理完后,left++,right--,
//直到不相等为止
//其中i的处理需要额外考虑相同值的问题,返回的三元组是会出现相同的值的[nums[i],nums[j],nums[k]]
//其中允许出现 nums[i]==nums[j],所以不能i与后面的相等直接就continue,需要考虑相同值的问题,
//先要让i处理了一次后再continue
Arrays.sort(nums);
List<List<Integer>> resultList = new ArrayList<List<Integer>>();
for(int i=0;i<nums.length;i++){
//这个点没想到
if(nums[i]>0){
return resultList;
}
int left = i+1;
int right = nums.length-1;
//去重处理,注意nums[i]==nums[i-1]这个判定,注定i必须要执行了一次后再进行去重处理,要考虑重复的nums[i]也满足
if(i>0&&nums[i]==nums[i-1]){
continue;
}
while(left<right){
int sum = nums[i]+nums[left]+nums[right];
if(sum>0){
right--;
}else if(sum<0){
left++;
}else{
//等于0时记录这三个数同时让left和right去重
resultList.add(Arrays.asList(nums[i], nums[left], nums[right]));
/*List<Integer> list = new ArrayList<Integer>();
list.add(nums[i]);
list.add(nums[left]);
list.add(nums[right]);
resultList.add(list);*/
//去重操作
while(left<right&&nums[left]==nums[left+1]) left++;
while(left<right&&nums[right]==nums[right-1]) right--;
//注意这里最后还要进行一次left++和right--,因为left之后指的位置应该是下一个元素了
//但是上面的while循环走完,nums[left]!=nums[left+1]时,我们应该把指针指向left+1所以还要走一位
left++;
right--;
}
}
}
return resultList;
}
}
四数之和
建议: 要比较一下,本题和 454.四数相加II 的区别,为什么 454.四数相加II 会简单很多,这个想明白了,对本题理解就深刻了。 本题 思路整体和 三数之和一样的,都是双指针,但写的时候 有很多小细节,需要注意,建议先看视频。
题目链接/文章讲解/视频讲解:代码随想录
看到题目的第一想法
和三数之和一样,不过要加个for
当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。
看到代码随想录之后的想法
只有当nums[i]>0且nums[i]>target时,才剪枝,同样进行剪枝(i>0&&nums[i]>target)和
需要给第二个for循环里面的j 去重(j>i+1&&nums[j]==nums[j-1])操作
自己实现过程中遇到的困难
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
//四数相加II返回的是个数,而这个返回的是每个内容
//四数相加II 是有四个数组
//在三数之和上再加一个循环?ij,left right?
//三个不同的下标,同时满足nums[i]+nums[j]+nums[k]==0
//哈希做法,先存入map再双重循环?
//卡哥做法,用双指针来实现,先对数组排序
//1双指针,一个指针指向i,另外设置left和right指针,left指向i的后一个指针,right指向数组的最后位置
//来进行相加操作来分别判断
//其中题干要求返回四元组,但是数值不能重复(返回的是数值数组不是下标)
//需要考虑去重问题,当i与后面的相等,重复值不用处理了,往后continue,当left和right处理完后,left++,right--,
//直到不相等为止
//其中i的处理需要额外考虑相同值的问题,返回的三元组是会出现相同的值的[nums[i],nums[j],nums[k]]
//其中允许出现 nums[i]==nums[j],所以不能i与后面的相等直接就continue,需要考虑相同值的问题,
//先要让i处理了一次后再continue
//j也同理,需要考虑j去重的问题j>i+1&&nums[j]==nums[j-1]
Arrays.sort(nums);
List<List<Integer>> resultList = new ArrayList<List<Integer>>();
for(int i=0;i<nums.length;i++){
//这个点没想到,剪枝操作,负数不用这样考虑,正数可以
if(nums[i]>0&&nums[i]>target){
return resultList;
}
//去重处理,注意nums[i]==nums[i-1]这个判定,注定i必须要执行了一次后再进行去重处理,要考虑重复的nums[i]也满
if(i>0&&nums[i]==nums[i-1]){
continue;
}
for(int j=i+1;j<nums.length;j++){
int left = j+1;
int right = nums.length-1;
//下面这个j>i+1如果不加的话也会报错,如果没加这个判断,j的第一步可能没有考虑到去重的
if(j>i+1&&nums[j]==nums[j-1]){
continue;
}
while(left<right){
int sum = nums[i]+nums[j]+nums[left]+nums[right];
if(sum>target){
right--;
}else if(sum<target){
left++;
}else{
//等于0时记录这三个数同时让left和right去重
resultList.add(Arrays.asList(nums[i], nums[j],nums[left], nums[right]));
/*List<Integer> list = new ArrayList<Integer>();
list.add(nums[i]);
list.add(nums[left]);
list.add(nums[right]);
resultList.add(list);*/
//去重操作
while(left<right&&nums[left]==nums[left+1]) left++;
while(left<right&&nums[right]==nums[right-1]) right--;
//注意这里最后还要进行一次left++和right--,因为left之后指的位置应该是下一个元素了
//但是上面的while循环走完,nums[left]!=nums[left+1]时,我们应该把指针指向left+1所以还要走一位
left++;
right--;
}
}
}
}
return resultList;
}
}