方法一:暴力(超时)

1.解题思路

题意:
对于一个给定的数组,数组元素有正有负有0,求所有连续子数组元素和为k中,最长的子数组。

2.解法

暴力解法为两层循环遍历序列,分别枚举左右端点。对子数组累加求和为k的保存并比较其长度,从而得到最长子序列的长度。

3.具体代码

class Solution {
public:
    /**
     * max length of the subarray sum = k
     * @param arr int整型vector the array
     * @param k int整型 target
     * @return int整型
     */
    int maxlenEqualK(vector<int>& arr, int k) {
        if(arr.empty())return 0;
        int ans=0;
        for(int i=0;i<arr.size();i++){//子数组左端点 
            int sum=0;
            for(int j=i;j<arr.size();j++){//累加求和 
                sum+=arr[j];
                if(sum==k)ans=max(ans,j-i+1);//若找到等于k,则更新最大长度 
            }
        }
        return ans; 
    }
};

4.时间复杂度和空间复杂度分析

时间复杂度:,两层循环遍历长度为n的序列
空间复杂度:,仅用到了两个int型变量sum,ans,来记录长度。

方法二:哈希表

1.解题思路

根据累加求和的思想,可以先记录序列中的前n项和,再做差得出子数组和。因为先找子序列和,再找子序列长度进行比较,所以可以把子序列和与数组元素下标做出映射关系,这里选用unordered_map,不用对容器内元素进行排序。

2.解法

  • i为遍历的当前元素下标,即要做子数组的前一个元素的下标
  • sum[i]即为要做子数组的前一个元素的前n项和
  • sum[i]+k即为要找子数组的右端点的前n项和
  • m[sum[i]+k]即为要找子数组的右端点的下标
  • m[sum[i]+k]-i即要找子数组的长度

但是,这种方法只考虑到了要找子数组从第二个元素开始的情况
所以,最后要加上要找子数组从第一个元素开始的情况

3.图解

图片说明

4.具体代码

class Solution {
public:
    /**
     * max length of the subarray sum = k
     * @param arr int整型vector the array
     * @param k int整型 target
     * @return int整型
     */
    int maxlenEqualK(vector<int>& arr, int k) {
        if(!arr.size())return 0;
        vector<int>sum(arr.size(),0);//保存前n项和
        unordered_map<int,int>m;//前n项和,对应的下标,unordered_map不用按键排序     
        sum[0]=arr[0];//计算sum的值 
        int size=arr.size();
        for(int i=0;i<size;i++){
            sum[i]=sum[i-1]+arr[i];
            m[sum[i]]=i;//map查找键方便,我们从子数组和推出对应端点下标 
        } 
        int ans=0;
        for(int i=0;i<size;i++){
            if(m.find(sum[i]+k)!=m.end())ans=max(ans,m[sum[i]+k]-i);//由前n项和在map中找出刚好大k的,即右端点 
        } 
        if(m.find(k)!=m.end())ans=max(ans,m[k]+1);//如果有前n项和为k,即子数组为前n项的,再比下ans 
        return ans;
    }
};

5.时间复杂度和空间复杂度分析

时间复杂度:,n是序列长度,因为仅遍历了两次序列。
空间复杂度:,需要存储长度为n的哈希表完成前缀合与子序列下标的映射。