C++算法(4)前缀和

1.一维前缀和【模板】

【模板】前缀和_牛客题霸_牛客网

暴力解法的时间复杂度为

前缀和:快速求出数组中某一个连续区间的和,时间复杂度是

step1:预处理出一个前缀和数组

dp[i]表示[1,i]区间内所有元素的和,dp无需遍历求和,dp[i]=dp[i-1]+arr[i],dp[0]=0;

索引从1开始是为了避免处理边界情况

step2:使用前缀和数组

求[l,r]之间元素的和,用dp[r]-dp[l-1]

代码如下:

cpp 复制代码
#include <iostream>
using namespace std;
#include <vector>
int main() 
{
    int n,m;
    int l,r;
    cin>>n>>m;
    vector<int> arr(n+1);
    for(int i=1;i<n+1;i++)
    {
        cin>>arr[i];
    }
    vector<long long> dp(n+1);//防止溢出
    for(int i=1;i<n+1;i++)
    {
        dp[i]=dp[i-1]+arr[i];
    }
    while(m)
    {
        cin>>l>>r;
        cout<<dp[r]-dp[l-1]<<endl;
        m--;
    }
    return 0;
}

2.二维前缀和【模板】

【模板】二维前缀和_牛客题霸_牛客网

暴力解法的时间复杂度是

前缀和的时间复杂度是

step1预处理出一个前缀和矩阵:

d[i][j]表示从[1,1]到[i,j]这段区间内所有元素的和

dp[i][j]=A+B+C+D=++A+B++ +++A+C+++D-A=dp[i-1][j]+dp[i][j-1]+arr[i][j]-dp[i-1][j-1]

step2使用前缀和矩阵

x1,y1\]\~\[x2,y2

D=A+B+C+D-(A+B)-(A+C)+A=dp[x2][y2]-dp[x1-1][y2]-dp[x2][y1-1]+dp[x1-1][y1-1]

代码如下:

cpp 复制代码
#include <iostream>
using namespace std;
#include <vector>
int main() 
{
    int n,m,q;
    cin>>n>>m>>q;
    int x1,y1,x2,y2;
    vector<vector<long long>> arr(n+1,vector<long long>(m+1));
    vector<vector<long long>> dp(n+1,vector<long long>(m+1));
    for(int i=1;i<n+1;i++)
    {
        for(int j=1;j<m+1;j++)
        {
            cin>>arr[i][j];
        }
    }
    for(int i=1;i<n+1;i++)
    {
        for(int j=1;j<m+1;j++)
        {
            dp[i][j]=dp[i][j-1]+dp[i-1][j]-dp[i-1][j-1]+arr[i][j];
        }
    }
    while(q)
    {
        cin>>x1>>y1>>x2>>y2;
        cout<<dp[x2][y2]-dp[x1-1][y2]-dp[x2][y1-1]+dp[x1-1][y1-1]<<endl;
        q--;
    }
    return 0;
   
}

3.寻找数组的中心下标

724. 寻找数组的中心下标 - 力扣(LeetCode)

f:前缀和数组f[i]表示[0,i-1]区间内所有元素的和 f[i]=f[i-1]+nums[i-1]

g:后缀和数组:g[i]表示[i+1,n-1]区间所有元素的和g[i]=g[i+1]+nums[i+1]

细节问题:f(0)=0,从左向右,g(n-1)=0,从右向左

代码如下:

cpp 复制代码
class Solution {
public:
    int pivotIndex(vector<int>& nums) 
    {
        int n=nums.size();
        vector<int> f(n),g(n);
        for(int i=1;i<n;i++)
        {
            f[i]=f[i-1]+nums[i-1];
        }
        for(int i=n-2;i>=0;i--)
        {
           g[i]=g[i+1]+nums[i+1];
        }
        for(int i=0;i<n;i++)
        {
            if(f[i]==g[i])
            {
                return i;
            }
        }
        return -1;      
    }
};

还有另一种方法:

cpp 复制代码
class Solution {
public:
    int pivotIndex(vector<int>& nums) 
    {
        int n=nums.size();
        vector<int> dp(n+1);
        for(int i=1;i<n+1;i++)
        {
            dp[i]=dp[i-1]+nums[i-1];
        }
        for(int i=1;i<n+1;i++)
        {
            if(dp[i-1]==(dp[n]-dp[i]))
            {
                return i-1;
            }
        }
        return -1;      
    }
};

4.除自身以外数组的乘积

238. 除了自身以外数组的乘积 - 力扣(LeetCode)

f[i]表示[0,i-1]区间内所有元素的乘积,f[i]=f[i-1]*nums[i-1],f[0]=1 ,g[i]表示[i+1,n-1]区间内所有元素的乘积,g[i]=g[i+1]*nums[i+1],g[n-1]=1

代码如下:

cpp 复制代码
class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) 
    {   
        int n=nums.size();
        vector<int> f(n),g(n),anwer(n);
        f[0]=1,g[n-1]=1;
        for(int i=1;i<n;i++)
        {
            f[i]=f[i-1]*nums[i-1];
        }
        for(int i=n-2;i>=0;i--)
        {
            g[i]=g[i+1]*nums[i+1];
        }
        for(int i=0;i<n;i++)
        {
            anwer[i]=f[i]*g[i];
        }
        return anwer;
    }
};

5.和为K的子数组

560. 和为 K 的子数组 - 力扣(LeetCode)

不能用滑动窗口,因为这里的数组元素包括0和负数,找到一个区间,而right不往后退,若中间还有一个区间会被漏掉。

要在以i位置为结尾的所有子数组中找和为K的,即在[0,i-1]区间内,有多少个前缀和等于sum[i]-k(用哈希表储存<int,int>),如果sum[i]-k==0,就会去找前缀和为0的数组,但[0,-1]区间不存在,所以要提前把hash[0]=1;

用一个变量sum来标记前一个位置的前缀和。

如果sum-k这个值在哈希表中存在,说明之前有hash[sum-k]个前缀和等于sum-k。

代码如下:

cpp 复制代码
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) 
    {
        int n=nums.size();
        unordered_map<int,int> hash;
        hash[0]=1;  
        int sum=0,ret=0;
        for(auto x:nums)
        {
            sum+=x;
            if(hash.count(sum-k)) ret+=hash[sum-k];
            hash[sum]++;
        }
        return ret;
    }
};

6.和可被K整除的子数组

974. 和可被 K 整除的子数组 - 力扣(LeetCode)

①同余定理:

(a-b)/p=k......0->a%p=b%p

(a-b)/p=k a=b+pk a%p=(b+pk)%p=b%p
②负数%正数的结果及修正:

负%正=负 a%p+p=(a%p+p)%p

如果不修正,会认为4%5和-1%5是两个不同的余数,实际上是模5共余的(-1+5=4)

(sum-x)%k=0->sum%k=x%k,sum%k要修正为(sum%k+k)%k

代码如下:

cpp 复制代码
class Solution {
public:
    int subarraysDivByK(vector<int>& nums, int k) 
    {
        unordered_map<int,int> hash;
        int sum=0,ret=0;
        hash[0%k]=1;
        for(auto x:nums)
        {
            sum+=x;
            int r=(sum%k+k)%k;
            if(hash.count(r)) ret+=hash[r];
            hash[r]++;
        }
        return ret;
    }
};

7.连续数组

525. 连续数组 - 力扣(LeetCode)

可以把所有0改成-1,在数组中找出最长的子数组,使子数组中所有元素的和为0

如果前一个已经是sum[i],后一个必定短于前一个,不用记录。

每次循环(每到一个i),检查一下哈希表中是否有此前缀和,如果有,更新结果,且不用更新哈希表中此前缀和的下标,如果没有,更新哈希表中此前缀和的下标

sum[i]的最大范围是[0,i-1],当它为[0,-1]时情况也应是存在的,所以手动给hash[0]=-1;

len=i-下标

代码如下:

cpp 复制代码
class Solution {
public:
    int findMaxLength(vector<int>& nums) 
    {
        unordered_map<int,int> hash;
        int ret=0,sum=0;
        hash[0]=-1;
        for(int i=0;i<nums.size();i++)
        {
            sum+=nums[i]==0?-1:1;
            if(hash.count(sum)) ret=max(ret,i-hash[sum]);
            else hash[sum]=i;
        }
        return ret;
    }
};

8.矩阵区域和

1314. 矩阵区域和 - 力扣(LeetCode)

首先需要一个dp数组,填充时注意下标的映射关系。

但经过k的扩展之后有可能越出m×n矩阵,题目要求的是(x1,y1)和(x2,y2)之间元素的和,且这些元素都要在mat内。

+1是为了在使用时满足映射关系。

代码如下:

cpp 复制代码
class Solution 
{
public:
    vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k)
    {
        int m=mat.size(),n=mat[0].size();
        vector<vector<int>> dp(m+1,vector<int>(n+1));
        vector<vector<int>> answer(m,vector<int>(n));
        for(int i=1;i<m+1;i++)
        for(int j=1;j<n+1;j++)
        {
            dp[i][j]=dp[i][j-1]+dp[i-1][j]-dp[i-1][j-1]+mat[i-1][j-1];
        }
        for(int i=0;i<m;i++)
        for(int j=0;j<n;j++)
        {
            int x1=max(0,i-k)+1;
            int y1=max(0,j-k)+1;
            int x2=min(m-1,i+k)+1;
            int y2=min(n-1,j+k)+1;
            answer[i][j]=dp[x2][y2]-dp[x1-1][y2]-dp[x2][y1-1]+dp[x1-1][y1-1];
        }
        return answer;
    }
};
相关推荐
froginwe112 小时前
Java 条件语句
开发语言
君生我老2 小时前
C++ 多态
c++
不想看见4042 小时前
Minimum Path Sum 基本动态规划:二维--力扣101算法题解笔记
算法·leetcode·动态规划
啊阿狸不会拉杆2 小时前
《计算机视觉:模型、学习和推理》第 7 章-复杂数据密度建模
人工智能·python·学习·算法·计算机视觉·t分布·复杂数据密度建模
沐知全栈开发2 小时前
备忘录模式(Memento Pattern)
开发语言
wuqingshun3141592 小时前
大致说一下程序、进程、线程
java·运维·服务器·开发语言
Loo国昌2 小时前
【AI应用开发实战】00_StockPilotX技术博客专栏:从零构建生产级AI金融分析系统
人工智能·算法·语言模型·自然语言处理·金融·prompt
wuqingshun3141592 小时前
Object有哪些方法,大致说一下每个方法的含义?
java·开发语言·jvm
NGC_66112 小时前
Dijkstra算法
算法