前缀和通常可以用来求子数组的数组和或是乘数总和这类的题
presum[0] = 0;
for(int i = 1; i < numsSize; i++) {
presum[i] = nums[i-1] + presum[i-1];
}
724. 寻找数组的中心下标
数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。
如果中心下标位于数组最左端,那么左侧数之和视为
0
,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回
-1
。输入:nums = [1, 7, 3, 6, 5, 6] 输出:3 解释: 中心下标是 3 。 左侧数之和 sum = nums[0] + nums[1] + nums[2] = 1 + 7 + 3 = 11 , 右侧数之和 sum = nums[4] + nums[5] = 5 + 6 = 11 ,二者相等。
int pivotIndex(int* nums, int numsSize) {
int sum=0;
int presum=0;
for(int i=0;i<numsSize;++i) sum+=nums[i];
for(int i=0;i<numsSize;++i){
//枚举中心下标i
if(presum+nums[i]==sum-presum){
return i;
}
presum+=nums[i];
}
return -1;
}
523. 连续的子数组和
给你一个整数数组
nums
和一个整数k
,请你统计并返回 该数组中和为k
的子数组的个数 。子数组是数组中元素的连续非空序列。
输入:nums = [1,1,1], k = 2 输出:2
int subarraySum(int* nums, int numsSize, int k) {
int ans=0;
int* presum=malloc(sizeof(int)*(numsSize+1));
presum[0]=0;
for(int i=1;i<=numsSize;++i){
presum[i]=presum[i-1]+nums[i-1];
}
for(int i=0;i<=numsSize;++i){
for(int j=i+1;j<=numsSize;++j){
if(presum[j]-presum[i]==k){
ans++;
}
}
}
return ans;
}
238. 除自身以外数组的乘积
给你一个整数数组
nums
,返回 数组answer
,其中answer[i]
等于nums
中除nums[i]
之外其余各元素的乘积 。题目数据 保证 数组
nums
之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。请 不要使用除法,且在
O(n)
时间复杂度内完成此题。输入: nums = [1,2,3,4] 输出: [24,12,8,6]
int* productExceptSelf(int* nums, int numsSize, int* returnSize) {
int* presum=malloc(sizeof(int)*(numsSize+1));
int k=1;
for(int i=0;i<numsSize;++i){
presum[i]=k;
k*=nums[i];
}
k=1;
for(int i=numsSize-1;i>=0;--i){
presum[i]=presum[i]*k;
k*=nums[i];
}
*returnSize=numsSize;
return presum;
}
523. 连续的子数组和
给你一个整数数组
nums
和一个整数k
,编写一个函数来判断该数组是否含有同时满足下述条件的连续子数组:子数组大小 至少为 2 ,且
子数组元素总和为
k
的倍数。如果存在,返回
true
;否则,返回false
。如果存在一个整数
n
,令整数x
符合x = n * k
,则称x
是k
的一个倍数。0
始终视为k
的一个倍数。输入:nums = [23,2,4,6,7], k = 6 输出:true 解释:[2,4] 是一个大小为 2 的子数组,并且和为 6 。
bool checkSubarraySum(int* nums, int numsSize, int k){
int dp[numsSize];
dp[0]=nums[0];
for(int i=1;i<numsSize;++i){
if(nums[i]==0&&nums[i-1]==0){
return true;
}
dp[i]=dp[i-1]+nums[i];
if(dp[i]%k==0&&i>=1){
return true;
}
}
for(int i=0;i<numsSize;++i){
if(dp[i]<k) continue;
for(int j=0;j<i;++j){
if((dp[i]-dp[j])%k==0&&(i-j)>1){
return true;
}
}
}
return false;
}
560. 和为 K 的子数组
给你一个整数数组
nums
和一个整数k
,请你统计并返回 该数组中和为k
的子数组的个数 。子数组是数组中元素的连续非空序列。
输入:nums = [1,1,1], k = 2 输出:2
暴力法:两重循环遍历求
int subarraySum(int* nums, int numsSize, int k) {
int ans=0;
for(int i=0;i<numsSize;++i){
int sum=0;
for(int j=i;j<numsSize;++j){
sum+=nums[j];
if(sum==k){
ans++;
}
}
}
return ans;
}
前缀和:依然超时
bool checkSubarraySum(int* nums, int numsSize, int k){
int dp[numsSize];
dp[0]=nums[0];
for(int i=1;i<numsSize;++i){
if(nums[i]==0&&nums[i-1]==0){
return true;
}
dp[i]=dp[i-1]+nums[i];
if(dp[i]%k==0&&i>=1){
return true;
}
}
for(int i=0;i<numsSize;++i){
if(dp[i]<k) continue;
for(int j=0;j<i;++j){
if((dp[i]-dp[j])%k==0&&(i-j)>1){
return true;
}
}
}
return false;
}
1413. 逐步求和得到正数的最小值
给你一个整数数组
nums
。你可以选定任意的 正数 startValue 作为初始值。你需要从左到右遍历
nums
数组,并将 startValue 依次累加上nums
数组中的值。请你在确保累加和始终大于等于 1 的前提下,选出一个最小的 正数 作为 startValue 。
输入:nums = [-3,2,-3,4,2] 输出:5 解释:如果你选择 startValue = 4,在第三次累加时,和小于 1 。
int minStartValue(int* nums, int numsSize) {
int mmin=INT_MAX;
int presum=0;
for(int i=0;i<numsSize;++i){
presum+=nums[i];
if(presum<mmin) mmin=presum;
}
if(mmin<0){
mmin=1-mmin;
}else{
mmin=1;
}
return mmin;
}
1422. 分割字符串的最大得分
给你一个由若干 0 和 1 组成的字符串
s
,请你计算并返回将该字符串分割成两个 非空 子字符串(即 左 子字符串和 右 子字符串)所能获得的最大得分。「分割字符串的得分」为 左 子字符串中 0 的数量加上 右 子字符串中 1 的数量。
输入:s = "011101" 输出:5
int maxScore(char* s) {
int mmax=0;
int zero=0,one=0;
int n=strlen(s);
if(s[0]=='0') zero=1;
//统计1的总数
for(int i=1;i<n;++i){
if(s[i]=='1') one++;
}
int cur=0;//记录当前1的个数
for(int i=1;i<n;++i){
//当前0加上后面1的个数
if(zero+one-cur>mmax){
mmax=zero+one-cur;
}
if(s[i]=='0') zero++;
else cur++;
}
return mmax;
}
1524. 和为奇数的子数组数目
给你一个整数数组
arr
。请你返回和为 奇数 的子数组数目。由于答案可能会很大,请你将结果对10^9 + 7
取余后返回。输入:arr = [1,3,5] 输出:4 解释:所有的子数组为 [[1],[1,3],[1,3,5],[3],[3,5],[5]] 。所有子数组的和为 [1,4,9,3,8,5]. 奇数和包括 [1,9,3,5] ,所以答案为 4 。
前缀和解法:时间超过
int numOfSubarrays(int* arr, int arrSize) {
int* presum=malloc(sizeof(int)*(arrSize+1));
presum[0]=0;
for(int i=1;i<=arrSize;++i){
presum[i]=presum[i-1]+arr[i-1];
}
int ans=0;
for(int i=0;i<=arrSize;++i){
for(int j=i+1;j<=arrSize;++j){
if((presum[j]-presum[i])%2==1){
ans++;
}
}
}
return ans;
}
1588. 所有奇数长度子数组的和
给你一个正整数数组
arr
,请你计算所有可能的奇数长度子数组的和。子数组 定义为原数组中的一个连续子序列。
请你返回
arr
中 所有奇数长度子数组的和 。输入:arr = [1,4,2,5,3] 输出:58 解释:所有奇数长度子数组和它们的和为: [1] = 1 [4] = 4 [2] = 2 [5] = 5 [3] = 3 [1,4,2] = 7 [4,2,5] = 11 [2,5,3] = 10 [1,4,2,5,3] = 15 我们将所有值求和得到 1 + 4 + 2 + 5 + 3 + 7 + 11 + 10 + 15 = 58
int sumOddLengthSubarrays(int* arr, int arrSize){
int sum=0;
int presum[arrSize+1];
presum[0]=0;
for(int i=1;i<=arrSize;++i){
presum[i]=presum[i-1]+arr[i-1];
}
//边界处理很复杂
for(int i=0;i<=arrSize;++i){
for(int l=1;l+i<=arrSize;l+=2){
sum+=presum[i+l]-presum[i];
}
}
return sum;
}
2256. 最小平均差
给你一个下标从 0 开始长度为
n
的整数数组nums
。下标
i
处的 平均差 指的是nums
中 前i + 1
个元素平均值和 后n - i - 1
个元素平均值的 绝对差 。两个平均值都需要 向下取整 到最近的整数。请你返回产生 最小平均差 的下标。如果有多个下标最小平均差相等,请你返回 最小 的一个下标。
注意:
- 两个数的 绝对差 是两者差的绝对值。
n
个元素的平均值是n
个元素之 和 除以(整数除法)n
。0
个元素的平均值视为0
。输入:nums = [2,5,3,9,5,3] 输出:3
int minimumAverageDifference(int* nums, int numsSize) {
long long sum=0;
for(int i=0;i<numsSize;++i) sum+=nums[i];
int mmaxidx=-1;
long long mmaxdiff=INT_MAX;
long long tmpsum=0;
long long tmpdiff=0;
//枚举下标i
for(int i=0;i<numsSize;++i){
tmpsum+=nums[i];//记录前缀和
//如果有后续元素
if((numsSize-i-1)!=0){
tmpdiff=abs(tmpsum/(i+1)-((sum-tmpsum)/(numsSize-i-1)));
}else{
//如果没有后续元素
tmpdiff=abs(tmpsum/(i+1));
}
//如果遇到更大的绝对值之差
if(tmpdiff<mmaxdiff){
mmaxidx=i;
mmaxdiff=tmpdiff;
}
}
return mmaxidx;
}