Leetcode刷题-454:四数相加 II

1.题目描述

给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:0 <= i, j, k, l < n

nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

示例1:

输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
输出:2
解释两个元组如下:
(0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
(1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0

示例2:

输入:nums1 = [0], nums2 = [0], nums3 = [0], nums4 = [0]
输出:1

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/4sum-ii

2.题目分析

  • 注意题目描述中的返回值。是不需要我们记录下标值或者返回元组的,所以我们就只需要考虑满足加和为0的元组个数。

我们观察题目,正常思考,还是首先以穷举的思路进行思考,但是穷举好像貌似有点艰难,毕竟是四重循环,正常的coder应该不敢这么刚,所以我们再考虑能否对这四层循环进行一些压缩减少。但是无论怎么思考,类似四次的加和操作是不能避免的,那么我们还能否降重呢?

答案是肯定的,我们类比求两数之和a+b=0的过程,其中是对每个a进行存储,然后查找是否有-a。这里我们借鉴一下,我们可以考虑采用拆分的思想,将四个数组分开处理,每两个数组合并为一个数组,然后对合并后的两个数组进行求两数之和的操作,这样循环貌似就降到了两重。

这里的两个长度为n数组nums1nums2的合并之后就变成了一个n*n的大数组nums,如果我们对两个数组进行全部存储的话貌似也有点浪费空间,所以我们依然借鉴求两数之和的思路,只存储其中两个数组nums1nums2构成的组合nums,然后直接穷举处理另外两个数组nums3nums4看其组成的元组相加的和是否能与nums中的元素相加为0.

基本解题框架已经出来了,我们再考虑以下能否优化一点,我们看到上面的给出的解题过程在数组的分组合并之后,后面两个数组的操作的时候需要频繁的查找符合条件的元素,所以我们应该使用一种高效的数据结构,这时我们想到基于哈希的set或者map。这时应该选择哪个呢?我们知道这两个集合都是不允许重复的,所以我们如果选择set进行保存nums1nums2的元素之和的话,那么和的个数就无法确定,所以我们选择map来进行辅助存储,我们将其的k-v对定义为两数之和-该和出现次数。这样我们在后两个数组nums3nums4的遍历时就可以累加元组数目。

具体解题步骤如下:

  1. 首先将四个数组分为两个部分,nums1nums2为一组,nums3nums4为一组
  2. 初始化计数值sum,初始化unordered_map保存数组nums1nums2的所有加和的值与对应的数量
  3. nums3nums4遍历,若有nums3[k]+nums4[l]的值的相反数存在于unordered_map,则更新sum,sum += -(nums3[k]+nums4[l])的出现次数;
  4. 循环完成后返回sum

3.题目解答

哈希集合解法

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        int n= nums1.size();
        //初始化返回值与辅助哈希集合
        int num = 0;
        unordered_map<int,int> imap;
        //将前两个数组的所有情况下的相加之和作为一个map存储
        //其中k-v对的含义是:两数之和-该和出现次数
        for(int i = 0;i<n;i++){
            for(int j = 0;j<n;j++){
                imap[nums1[i]+nums2[j]]++;
            }
        }
        /*查看后两个数组的所有情况下的相加之和
        是否可以与前面两个数组的和相加为0*/
        for(int k = 0;k<n;k++){
            for(int l = 0;l<n;l++){
                if(imap.find(0-(nums3[k]+nums4[l])) != imap.end()){
                    num+=imap[0-(nums3[k]+nums4[l])];
                }
            }
        }
        return num;
    }
};

总结:问题需要的返回值有时可以决定难易程度。一般问题的解决都是从最简单的思路开始进行优化,如拆分合并双指针等思路。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值