一、454四数相加II
这道题首先要明白四个数相加,那么可以分为两组,前两个数组A和B,后两个数组C和D,就类似于前两个数组得出来的和和后两个数据得出来的和加起来为0即可,例1+(-1)=0,所以分为两组
先在第一组中做计算AB和,再将和存到hashmap数组中,hashmap数组存sum还有sum出现的次数,利用两个for循环来计算sum=i+j,之后要把sum还有次数放在hashmap数组中,用到了这个函数put()和getOrDefault(),具体操作是map.put(sum,map.getOrDefault(sum,0)+1),这个函数的意思就是如果 sum
存在于映射中,那么原来的值将被获取并加 1,如果 sum
不存在,就从默认值0开始加 1(sum的次数默认是0,sum的次数加一)。
再对第二组进行操作,这里主要是定义了一个res来统计和第一组和是否相等的值,也就是说统计的是-(i+j)的值的次数,如果出现一次那么这个就是四数和为0的情况,比如说1+(-1)=0,即是统计后面组数的和(-1)的相反数(1)是否和第一组的值(1)相等,相等就是把这个值保存的结果次数取出来放到res中,有这样的几个就放几个,所以对res+=即可,最后返回res
代码如下:
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
Map<Integer,Integer> map = new HashMap();
for(int i:nums1)
{
for(int j:nums2)
{
int sum = 0;
sum = i+j;
map.put(sum,map.getOrDefault(sum,0)+1);
}
}
int res = 0;
for(int i:nums3)
{
for(int j:nums4)
{
res += map.getOrDefault(0-i-j,0);
}
}
return res;
}
}
二、383赎金信
这道题主要是要明白,题目中是想要用第二个数组来表示第一个数组,主要的思路就想把第一个数组的数以0-25数字的表示形式将a-z小写字母表示出来,统计他们的次数,所以直接用hash数组来表示
之后在第二个数组遍历的时候可以减掉这个次数,直到最后数组中保存的次数都为0或者-1,如果大于0,那就返回false
这道题主要遇到的困难点在将字符串转换为字符数组这里,用到了一个toCharArray()的函数,然后for循环遍历取出来,之后传入创建的数组hash[]中,注意这里一定要写成hash[i - 'a']的形式,不然会造成越界的情况,因为咱们当初创建字符数组的时候就是创建的26的大小,即int[] hash = new int[26]
for(char i :ransomNote.toCharArray()),这里hash[i]表示的是hash[97]的意思,自动会将字符转换为ASCII码来存储这个字符的次数,默认的情况下是0,从0开始+=1,这里搞清楚之后就明白为什么要hash[i - 'a']了
哦对 还得在最前面加一个判断条件,就是第一个字符串的长度大于第二个字符串的长度的时候,要直接返回false,因为这样的情况,第二个字符串在只能不重复使用字符的情况下,不会表示出来第一个字符串的
代码如下:
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
if(ransomNote.length() > magazine.length())
return false;
int[] hash = new int[26];
for(char i:ransomNote.toCharArray())
{
hash[i - 'a'] += 1;
}
for(char i:magazine.toCharArray())
{
hash[i - 'a'] -= 1;
}
for(int i:hash)
{
if(i>0)
{
return false;
}
}
return true;
}
}
三、15三数之和
这道题遇到的难点还挺多的,自己真正解决起来才发现很麻烦,从一开始的定义一个包含三元组集合的列表,List<List<Integer>> result = new ArrayList<>(),这个语句之前做的题都没有接触过,之后又定义了一个数组的操作,这个操作不需要进行对象的创建,是用Java自带的一个类Arrays,可以直接使用类调用类方法,所以这里直接Arrays.sort(nums),这一步操作也是没有接触过,所以需要死死牢记。
之后的操作就是去重a,因为这里用到了一个双指针,先定义i是for循环,也就是a。
然后是a的去重是因为数组已经提前排好序,所以在进行每一轮的时候a向后移动的时候就必须考虑一不一样,因为b和c就是在每一轮a的后面在移动,所以要去重a。
在for循环中,for(int i = 0;i<nums.length;i++){if(nums[i]>0){return result;} if(i>0 && nums[i] == nums[i-1]){continue;}},这里是对首先是判断a是否是大于0,因为基于这个已经排好序的前提,如果大于0的话,之后的和始终都不会是0。之后a进行去重的操作,如果a这一轮的值和上一轮的值相等,那么直接跳出此层循环用continue。
之后在下一步 也是在for循环中对b,c做处理,left=i+1,right取nums.length-1(这里注意下,nums.length和nums.length()的区别,nums.length是描述数组的长度,nums.length()是描述字符串的长度,这两者 在使用的时候不能混淆),此时就再打开一个循环while(left<right),在循环里定义一个sum = nums[i] + nums[left] + nums[right],然后就是根据sum来进行调整,指针移动
sum<0那么left++,sum>0那么right--,else就是sum=0,这个直接进行数据填充,放在数组里,这里用到了一个result.add(Arrrays.asList(nums[i],nums[left],nums[right])),这个asList函数也是第一次接触,在你的代码中,Arrays.asList(nums[i], nums[left], nums[right])
用于创建一个包含三个整数元素的列表,用于存储符合条件的三元组。这个列表后续可以用于存储多个三元组,以便在解决问题时更容易管理和访问结果数据。
下一步就是b、c去重,在将满足条件的三元组放入result之后,就要着手处理b、c的去重
while(right > left && nums[left] == nums[left + 1]) left++,这一步就是当b现在和b的下一位相等时,那么就将指针跳到下一位上,但是此时这一位已经处理完毕了,所以在去重操作的后边会统一再加一个left++,同理right操作也是这样处理,while(right > left && nums[right] == nums[right - 1]) right--,那么就将指针跳到下一位上,但是此时这一位已经处理完毕了,所以在去重操作的后边会统一再加一个right--
最后在for循环结束返回result即可
代码如下:
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
// if(nums[0]>0 && nums.length() == 0)
// {
// return result;
// }
for(int i = 0;i<nums.length;i++)
{
if(nums[i] >0 )
{
return result;
}
if(i> 0 && nums[i] == nums[i-1])
{
continue;
}
int left = i+1;
int right = nums.length - 1;
while(right > left)
{
int sum = nums[i] + nums[left] + nums[right];
if(sum < 0)
{
left++;
}else if(sum > 0)
{
right--;
}else{
result.add(Arrays.asList(nums[i],nums[left],nums[right]));
while(right > left && nums[left] == nums[left+1]) left++;
while(right > left && nums[right] == nums[right-1]) right--;
right--;
left++;
}
}
}
return result;
}
}
四、18四数之和
这道题首先是与上一道很类似,但是需要的是两个for循环,在新的for循环中,把j看作是i就可以了,总的来说,就是那几步,先创建ArrayList数组,然后用Arrays类进行排序,之后就是进入正常的for循环,在for循环里进行两步判断,第一步是nums[i]大于0,如果是大于target那么自然是不会有满足条件的四元组了,此时返回result空数组即可,第二步判断是对a进行去重,在i大于0之后那么就是判断他的这个位置是否是和前一个位置i-1相等,如果相等,那么就是重复元组
之后再进行一次for循环将i+1赋值给j,相当于j变成之前的i了,进行b去重操作,然后设置两个指针left,right与之前的过程都是一样的,只不过要注意的是sum是和target比较的,不是和0比较的,然后就是最后“通过在 else
语句块中执行 left++
和 right--
来确保只有在找到满足条件的四元组后才更新指针,以防止错误添加。这是为了保持逻辑的正确性。当 left++
和 right--
放在 else
语句块之外时,可能会导致逻辑错误。”,left++和right--本身就是三个if else在处理过程中的增减,不能将最后一个left++和right--放在最后一个else外边,这里做的时候卡了一下
还有一个要注意的问题,在sum=nums[i] + nums[j] + nums[left] + nums[right];要变成长整型
long sum =(long) nums[i] + nums[j] + nums[left] + nums[right];
原因如下,主要还是怕int类型范围溢出,因为是加的四个数,多了一个数
在
long sum =(long) nums[i] + nums[j] + nums[left] + nums[right];
中将sum
声明为long
类型的原因是为了防止整数溢出。整数溢出是指当两个int
类型的数相加,结果超过了int
类型能够表示的范围(通常是 -2^31 到 2^31-1),导致结果不准确或溢出。在这种情况下,
sum
是由四个int
类型的数相加而成,如果它们的和超过了int
类型的范围,就会导致溢出。因此,将sum
声明为long
类型可以扩大其表示范围,防止溢出。
代码如下 :
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
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] == nums[i-1])
{
continue;
}
for(int j = i + 1;j < nums.length;j++)
{
// if(nums[j] > 0 && nums[i] + nums[j] > target)
// {
// return result;
// }
if(j > i+1 && nums[j] == nums[j - 1])
{
continue;
}
int left = j+1;
int right = nums.length - 1;
while(right > left)
{
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]));
while(right > left && nums[left] == nums[left+1]) left++;
while(right > left && nums[right] == nums[right-1]) right--;
left++;
right--;
}
}
}
}
return result;
}
}