刷题day6 454四数相加II、383赎金信、15三数之和、18四数之和

文章讲述了如何通过迭代和哈希映射实现四数相加问题的解决方案,包括使用hashmap存储中间和及其出现次数,以及在后续数组中进行去重操作。同时介绍了383赎金信问题中字符串转换和字符数组的应用,以及15/18题的数组排序、去重和双指针技巧。
摘要由CSDN通过智能技术生成

一、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;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值