题目:
和为零的最大连续子数组
思路:
我首先想到的是前缀数组和,遍历一遍数组,计算出sum[i](表示从0-i的子数组之和)。
有了前缀数组和,只要sum[i]=sum[j](i<j),那么区间[i+1,j]就是和为零的子数组,只要在遍历前缀数组和时记录最长的区间即可。
需要注意的是:当sum[i]等于0时,其区间为[0,i]。
在判断sum[i]=sum[j](i<j)时,有个查找的过程,要么直接遍历j左边的所有数(增加时间复杂度),要么通过map来存储对应和的下标位置(空间换时间)。(详见代码)
代码:
#include<iostream> #include<vector> #include<map> using namespace std; // O(n^2) int longestSubArrayOfSumZero_1(const vector<int> &arr){ int sz=arr.size(); vector<int> preSum(sz+1,0); for(int i=0;i<sz;i++){ preSum[i+1]=preSum[i]+arr[i]; } int longest=0; int start=0; for(int i=1;i<=sz;i++){ for(int j=0;j<i;j++){ if(preSum[i]==preSum[j] && (i-j)>longest){ longest=i-j; start=j; } } } for(int i=start;i<start+longest;i++) cout<<arr[i]<<" "; cout<<endl; return longest; } // O(n) int longestSubArrayOfSumZero_2(const vector<int> &arr){ int sz=arr.size(); int longest=0; map<int,int> pos; int sum=0; pos[sum]=0; int start=0; for(int i=0;i<sz;i++){ sum+=arr[i]; if(pos.find(sum)!=pos.end()){ int len=i-pos[sum]; if(len>longest){ longest=len; start=pos[sum]+1; } } else pos[sum]=i; } for(int i=start;i<start+longest;i++) cout<<arr[i]<<" "; cout<<endl; return longest; } int main(){ int n; while(cin>>n){ vector<int> arr(n); for(int i=0;i<n;i++){ cin>>arr[i]; } cout<< longestSubArrayOfSumZero_1(arr) <<endl; cout<< longestSubArrayOfSumZero_2(arr) <<endl; } return 0; }