当前位置: 首页 > article >正文

【算法笔记】前缀和算法原理深度剖析(超全详细版)

【算法笔记】前缀和算法原理深度剖析(超全详细版)

🔥个人主页大白的编程日记

🔥专栏算法笔记


文章目录

  • 【算法笔记】前缀和算法原理深度剖析(超全详细版)
    • 前言
    • 一.一维前缀和
      • 1.1题目
      • 1.2算法原理解析
      • 1.3代码实现
    • 二.二维前缀和
      • 2.1题目
      • 2.2算法原理解析
      • 2.3下标映射
      • 2.4初始化问题
      • 2.5代码实现
    • 三.寻找数组的中心下标
      • 3.1题目
      • 3.2思路分析
      • 3.3代码实现
    • 四.除自身以外数组的乘积
      • 4.1题目
      • 4.2思路分析
      • 4.3总结
      • 4.4代码实现
    • 五.和为k的子数组
      • 5.1题目
      • 5.2思路分析
      • 5.3代码实现
    • 六.和可被k整除的子数组
      • 6.1题目
      • 6.4思路分析
      • 6.3代码实现
    • 七.连续数组
      • 7.1题目
      • 7.1思路分析
      • 7.3代码实现
    • 八.矩阵区域和
      • 8.1题目
      • 8.2思路分析
      • 8.3代码实现
    • 后言

前言

哈喽,各位小伙伴大家好!上期我们讲了二分算法。今天我们来讲前缀和的算法原理。话不多说,咱们进入正题!向大厂冲锋!

一.一维前缀和

1.1题目

  • 题目:【模板】前缀和

1.2算法原理解析

我们根据前缀和算法就可以快速求出区间和。

为了防止越界,我们要让前缀和数组下标从1开始。

1.3代码实现

#include <iostream>
#include<vector>
using namespace std;
int main() 
{
    int n,q;
    cin>>n>>q;
    vector<long long> dp(n+1);//多开一个节点防止越界
    int tmp=0;
    for(int i=1;i<=n;i++)
    {
        cin>>dp[i];
    }
    for(int i=1;i<=n;i++)
    {
        dp[i]+=dp[i-1];
    }
    int l,r;
    while(q--)
    {
        cin>>l>>r;
        cout<<dp[r]-dp[l-1]<<endl;
    }
}
// 64 位输出请用 printf("%lld")

二.二维前缀和

2.1题目

  • 题目:二维前缀和

2.2算法原理解析

2.3下标映射

2.4初始化问题

如果用到两个前缀和区间求某区间的和
我们初始化的值并不重要。
在这里插入图片描述

  • 验证

2.5代码实现

#include <iostream>
#include<vector>
using namespace std;

int main()
{
    int n, m, q;
    cin >> n >> m >> q;
    vector<vector<long long>> arr(n,vector<long long>(m));
    for (int i = 0; i <n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            cin >> arr[i][j];
        }
    }
    vector<vector<long long>> dp(n+1,vector<long long>(m + 1));
    for (int i = 1; i <=n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            dp[i][j] = dp[i][j - 1]+dp[i-1][j]-dp[i-1][j-1]+arr[i-1][j-1];
        }
    }
    while (q--)
    {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        long long sum=0;
        sum=dp[x2][y2]-dp[x2][y1-1]-dp[x1-1][y2]+dp[x1-1][y1-1];
        cout<<sum<<endl;
    }
}

三.寻找数组的中心下标

3.1题目

  • 题目:寻找数组的中心下标

3.2思路分析

这里我们借助前缀和数组和后缀和数组即可快速判断中心下标。

3.3代码实现

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]=nums[i-1]+f[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;
    }
};

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

4.1题目

  • 题目:除自身以外数组的乘积

4.2思路分析

4.3总结

4.4代码实现

class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) 
    {
        int n=nums.size();
        vector<int> f(n),g(n),ret(n);
        f[0]=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++)
        {
            ret[i]=f[i]*g[i];
        }
        return ret;
    }
};

五.和为k的子数组

5.1题目

  • 题目:和为k的子数组

5.2思路分析

5.3代码实现

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) 
    {
        unordered_map<int,int> hash;
        hash[0]=1;//整个区间和为k
        int sum=0,ret=0;
        for(auto e:nums)
        {
            sum+=e;//计算前缀和
            if(hash.count(sum-k))//统计和为sum-k区间个数
            {
                ret+=hash[sum-k];
            }
            hash[sum]++;//填入前缀和信息
        }
        return ret;
    }
};

六.和可被k整除的子数组

6.1题目

  • 题目:和可被k整除的子数组

6.4思路分析

关于更多的模运算知识可以看一下灵神的这篇讲解:
模运算的世界

6.3代码实现

class Solution {
public:
    int subarraysDivByK(vector<int>& nums, int k) 
    {
        unordered_map<int,int> hash;
        hash[0]=1;//整个区间和为k
        int sum=0,ret=0;
        for(auto e:nums)
        {
            sum+=e;//计算前缀和
            if(hash.count((sum%k+k)%k))//统计和为被k整除区间个数负数修正
            {
                ret+=hash[(sum%k+k)%k];
            }
            hash[(sum%k+k)%k]++;//填入前缀和%k信息
        }
        //(a-b)%p==a%p==b%p同余定理
        return ret;
    }
};

七.连续数组

7.1题目

  • 题目:连续数组

7.1思路分析

7.3代码实现

class Solution {
public:
    int findMaxLength(vector<int>& nums)
    {
        unordered_map<int,int> hash;
        hash[0]=-1;
        int sum=0,len=0;
        for(int i=0;i<nums.size();i++)
        {
            sum+=(nums[i]==0?-1:1);//0就变成-1;
            if(hash.count(sum-0))
            {
                len=max(len,i-hash[sum]);//更新长度
            }
            else//相同的前缀和不更新
            {
                hash[sum]=i;//更新哈希表前缀和信息
            }
        }
        return len;
    }
};

八.矩阵区域和

8.1题目

  • 题目:矩阵区域和

8.2思路分析

8.3代码实现

class Solution {
public:
    vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k)
    {
        int m=mat.size(),n=mat[0].size();
        vector<vector<int>> arr(m+1,vector<int>(n+1));
        for(int i=1;i<m+1;i++)//处理前缀和数组
        {
            for(int j=1;j<n+1;j++)
            {
                arr[i][j]=arr[i][j-1]+arr[i-1][j]-arr[i-1][j-1]+mat[i-1][j-1];
            }
        }
        vector<vector<int>> arr1(m,vector<int>(n));
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                int x1=max(0,i-k)+1;
                int x2=min(m-1,i+k)+1;
                int y1=max(0,j-k)+1;
                int y2=min(n-1,j+k)+1;//计算下标 +1映射dp数组
                arr1[i][j]=arr[x2][y2]-arr[x2][y1-1]-arr[x1-1][y2]+arr[x1-1][y1-1];
            }
        }
        return arr1;
    }
};

后言

这就是前缀和算法原理的深度剖析。大家自己好好消化理解。今天 就分享到这,感谢各位大耐心垂阅!咱们下期见!拜拜~


http://www.kler.cn/a/430178.html

相关文章:

  • [大模型]本地离线运行openwebui+ollama容器化部署
  • matlab编写分段Hermite插值多项式
  • kubeneters-循序渐进Cilium网络(二)
  • 详细分析 Git 分支重命名与同步操作
  • Photon最新版本PUN 2.29 PREE,在无网的局域网下,无法连接自己搭建的本地服务器
  • ubuntu22.04 的录屏软件有哪些?
  • gozero项目迁移与新服务器环境配置,包含服务器安装包括go版本,Nginx,项目配置包括Mysql,redis,rabbit,域名
  • 使用 Postman 上传二进制类型的图片到后端接口写法
  • 通俗易懂理解:网络安全恶意节点的检测与哨兵节点的激活【论文+代码】
  • 杨振宁大学物理视频中黄色的字,c#写程序去掉
  • net8 WebAP Swagger
  • JS中的原型链与继承
  • PyTorch张量的backward方法和.grad属性介绍
  • 鸿蒙Next开发实战教程-使用WebSocket实现即时聊天
  • 如何实现多级缓存以及缓存之间数据的一致性
  • vscode鼠标右键跳转到定义只能跳转到头文件
  • C++ 列表初始化(initializer_list)
  • Go validator验证参数是否零值以及是否传递
  • IDEA创建Spring Boot项目配置阿里云Spring Initializr Server URL【详细教程-轻松学会】
  • IO进程学习笔记
  • 最新 AI 编程工具全面对比:v0、Bolt.new、Cursor、Windsurf
  • 树莓派 PICO RP2040 MACOS 使用
  • ArcMap 分析面到线、线到线、面重叠等功能操作
  • SQL中IN和NOT操作符的用法
  • 概率论相关知识随记
  • 【大语言模型】LangChain LCEL 表达式语言