【算法精讲】一篇让你掌握前缀和算法(附图解和不少题目练习~~)

这篇具有很好参考价值的文章主要介绍了【算法精讲】一篇让你掌握前缀和算法(附图解和不少题目练习~~)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.简介

前缀和算法是一种用空间换时间的算法,他常常用于解决某些题目或者作为某些高级算法的组成部分。

例如:让你求某个矩阵(一维)的子矩阵的最大值,如果使用暴力解法它的时间复杂度将会是O(n^2) ,但如果使用该算法就可以使其时间复杂度降低一个维度也就是O(N).

2.一维前缀和

讲解

该算法需要开辟一个比原数组的大小大一个内存的数组

它的每一个元素意义是:前原数组n个元素的总和。也就是说下标为3的元素,是原来前三个元素的和。(也可以理解为除了自己以外的前面元素的和)

至于为什么会多开辟一个元素,我们后续会讲。
【算法精讲】一篇让你掌握前缀和算法(附图解和不少题目练习~~),算法,算法,数据结构
因此我们可以得出求和数组的递推公式

s u m [ i ] = a r r [ i − 1 ] + s u m [ i − 1 ] sum[i]=arr[i-1] + sum[i-1] sum[i]=arr[i1]+sum[i1]

此时我们多开一个内存的意义就可以体现出来了,当我们求第一个元素数组的时候需要加上前一个sum 。

但是如果第一个元素前一个位置没有东西就会发生越界访问,因此我们要给他提前准备一个内存并且默认为0

总的来说,就是为了处理第一个元素越界的问题。

求子矩阵内的和

我们已经知道了sum的每一个元素的意义,那么原数组的子矩阵的和也就可以得出来,例如:下标x到y的子矩阵之和就等于:

s o n = s u m [ y + 1 ] − s u m [ x ] son=sum[y+1]-sum[x] son=sum[y+1]sum[x]
【算法精讲】一篇让你掌握前缀和算法(附图解和不少题目练习~~),算法,算法,数据结构

代码

如果你理解了上述内容,它的代码就可以轻松写出来:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int arr[5] = {1,2,3,4,5};
	int sum[6];
	sum[0] = 0;

	for (int i = 1; i < 6; i++) sum[i] = arr[i - 1] + sum[i - 1];

	for (auto i : sum) cout << i << ' ';
	
	return 0;
}

运行结果:
【算法精讲】一篇让你掌握前缀和算法(附图解和不少题目练习~~),算法,算法,数据结构

3.二维前缀和

讲解

二维前缀和相对于一维复杂的多,它也需要多开辟空间,不同的是他是在每个维度都要多开辟一个。

它每个元素的意义是:下标为(x,y)的求和数组的元素,是原数组下标(0,0)到(x - 1, y - 1)子矩阵内元素的和,建议配合图来理解。

举个例子: sum[2][2] = arr[0][0] + arr[0][1] + arr[1][0] + arr[1][1] 其他也是如此。
【算法精讲】一篇让你掌握前缀和算法(附图解和不少题目练习~~),算法,算法,数据结构
如果你理解了上述内容,那么就让我们来推一下它的递推公式:
至于我们为什么要这么求,暂且往下看。
【算法精讲】一篇让你掌握前缀和算法(附图解和不少题目练习~~),算法,算法,数据结构

【算法精讲】一篇让你掌握前缀和算法(附图解和不少题目练习~~),算法,算法,数据结构
当我们求前缀和的时候,我们是顺序求取的,当我们求(x,y)位置的前缀和时候,我们的(x,y - 1),(x - 1, y -1),(x- 1,y)这三个位置的前缀和就已经求出来了,也就是说ABCD的位置的值你都是知道的,所以我们我么可以得出以下公式:

s u m [ x ] [ y ] = s u m [ x − 1 ] [ y ] + s u m [ x ] [ y − 1 ] − s u m [ x − 1 ] [ y − 1 ] + a r r [ x − 1 ] [ y − 1 ] sum[x][y] = sum[x-1][y] + sum[x][y-1] - sum[x-1][y-1] + arr[x- 1][y-1] sum[x][y]=sum[x1][y]+sum[x][y1]sum[x1][y1]+arr[x1][y1]

求子矩阵和

求子矩阵和是建立在已经求出所有前缀和基础上的,求子矩阵和的方法和求前缀和的方法相似,且看图解:
【算法精讲】一篇让你掌握前缀和算法(附图解和不少题目练习~~),算法,算法,数据结构
由此我们可以得出递推公式,原数组(x1,y1)到(x,y)的子矩阵:
s o n = s u m [ x + 1 ] [ y + 1 ] + s u m [ x 1 ] [ y 1 ] − s u m [ x + 1 ] [ y 1 ] − s u m [ x 1 ] [ y + 1 ] son =sum[x+1][y+1] +sum[x1][y1]-sum[x+1][y1]-sum[x1][y+1] son=sum[x+1][y+1]+sum[x1][y1]sum[x+1][y1]sum[x1][y+1]

代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
	vector<vector<int>>v(3, vector<int>(3));
	vector<vector<int>>sum(4, vector<int>(4, 0));

	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
		{
			v[i][j] = j + 1;//每个一维数组初始化为1 2 3
			sum[i + 1][j + 1] = sum[i + 1][j] + sum[i][j + 1] - sum[i][j] + v[i][j];//递推
		}
	
	for (auto i : sum)
	{
		for (auto k : i)
			cout << k << ' ';
		cout << endl;
	}

	int x1, y1,x,y;

	cout << "子矩阵坐标:" << endl;
	cin >> x1 >> y1 >> x >> y;//求子矩阵
	cout << sum[x + 1][y + 1] - sum[x + 1][y1] - sum[x1][y + 1] + sum[x1][y1];

	return 0;
}

运行结果:
【算法精讲】一篇让你掌握前缀和算法(附图解和不少题目练习~~),算法,算法,数据结构

4.题目

一维前缀和模板

【算法精讲】一篇让你掌握前缀和算法(附图解和不少题目练习~~),算法,算法,数据结构
点这里进入
答案:最简单的都不会???回去再翻翻去

二维前缀和模板

【算法精讲】一篇让你掌握前缀和算法(附图解和不少题目练习~~),算法,算法,数据结构
点这里进入
答案:这个做对了说明你理解这个算法了

#include<bits/stdc++.h>
#define int long long
using namespace std;
signed main() {
    int n = 0, m = 0, q = 0;
    cin >> n >> m >> q;

    vector<vector<int>> arr(n + 1, vector<int>(m + 1));
    vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));

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

寻找数组的中心下标

【算法精讲】一篇让你掌握前缀和算法(附图解和不少题目练习~~),算法,算法,数据结构

点吧你,一点一个不吱声

思路:

  1. 前缀和+后缀和(这个纯属就是前缀的倒序版)
  2. 如果这个元素的前缀和和后缀和相同,说明找到了。
class Solution {
public:
    int pivotIndex(vector<int>& nums) {

        vector<int>dp(nums.size() + 1, 0);
        vector<int>dp1(nums.size() + 1, 0);

        for (int i = 1; i <= nums.size(); i++) {
            dp[i] = dp[i - 1] + nums[i - 1];
        }
        for(int i = nums.size() - 2; i >= 0; i--){
            dp1[i] = dp1[i + 1] + nums[i + 1];
        }

        for (int i = 0; i < nums.size(); i++) {
            if (dp[i] == dp1[i])
                return i;
        }

        return -1;
    }
};

除自己以外数组的乘积

【算法精讲】一篇让你掌握前缀和算法(附图解和不少题目练习~~),算法,算法,数据结构

点我就完事了
思路:

  1. 前缀和算法的简单变化,本质依旧如此
  2. 可以使用上一个题的方法,前缀和+后缀和
  3. 下面的方法是那个方法的优化,空间复杂度优化到了O(1)
class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
       // int sum = accumulate(nums.begin(), nums.end(), 1, mulltiplies<int>());
        int n = nums.size();
        vector<int> ans(n);
        ans[0] = 1;

        for(int i = 1; i < n;i++)
            ans[i] = ans[i - 1] * nums[i - 1];
        
        int r = 1;
        for(int i = n - 1; i >= 0;i--){
            ans[i] = ans[i] * r;
            r *= nums[i];
        }

        return ans;
    }
};

和为k的子数组

【算法精讲】一篇让你掌握前缀和算法(附图解和不少题目练习~~),算法,算法,数据结构

挺难的
思路:
【算法精讲】一篇让你掌握前缀和算法(附图解和不少题目练习~~),算法,算法,数据结构

  1. 假设这个位置存在连续数组使其和为target,那么说明其前面的前缀和必存在sum - target的值
  2. 配合哈希表使用,记录前面每个前缀和出现的次数
  3. 从头开始遍历,一遍存前缀和,一边找是否有符合的数组
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        unordered_map<int, int> hash;
        int sum = 0;
        int ret = 0;

        for(auto i : nums)
        {
            hash[sum]++;
            sum += i;
            if(hash.count(sum - k)) ret += hash[sum - k];
        }
        return ret;
    }
};

感谢观众老爷的观看Thanks♪(・ω・)ノ,如果觉得满意的话留个关注再走吧。文章来源地址https://www.toymoban.com/news/detail-850044.html

到了这里,关于【算法精讲】一篇让你掌握前缀和算法(附图解和不少题目练习~~)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C标准库】详解fopen函数 一篇让你搞懂fopen函数

    创作不易,感谢支持! ‾ underline{创作不易,感谢支持! } 创作不易,感谢支持! ​ fopen函数 头文件:stdio.h 功能是打开一个文件,其声明格式是: 文件指针名 = fopen(文件名,使用文件方式) “文件名”是被打开文件的文件名,类型是C风格字符串。 “使用文件方式”是指文

    2024年02月03日
    浏览(37)
  • GPT引领学习之旅:一篇让程序员轻松掌握Elasticsearch的攻略

    随着大数据技术的飞速发展,程序员们面临着越来越多的挑战。Elasticsearch作为一款流行的开源搜索和分析引擎,已成为许多项目的重要组成部分。那么如何高效地学习并掌握Elasticsearch呢?在这篇文章中,我们将探讨如何运用GPT(Generative Pre-trained Transformer)技术,帮助程序员

    2024年02月02日
    浏览(62)
  • Python 语法及入门 (超全超详细) 专为Python零基础 一篇博客让你完全掌握Python语法

    前言: 本篇博客超级详细,请尽量使用电脑端结合目录阅读 阅读时请打开右侧 “只看目录”  方便阅读 1989 年,为了 打发 圣诞节假期,Gudio van Rossum吉多· 范罗苏姆(龟叔)决心开发一个新的解释程序( Python 雏形) 1991 年,第一个 Python 解释器诞生 Python 这个名字,来自

    2024年02月08日
    浏览(51)
  • 蓝桥杯统计子矩阵前缀和C++(附图文超详细讲解)(保姆级)

    给定一个 N × M 的矩阵 A,请你统计有多少个子矩阵 (最小 1 × 1,最大 N × M) 满足子矩阵中所有数的和不超过给定的整数 K?  第一行包含三个整数 N, M 和 K.  之后 N 行每行包含 M 个整数,代表矩阵 A. 一个整数代表答案。 满足条件的子矩阵一共有 19,包含: 大小为 1 × 1 的有

    2023年04月08日
    浏览(38)
  • 学习javascript,前端知识精讲,助力你轻松掌握

    ✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 所属专栏: 前端泛海 景天的主页: 景天科技苑 JavaScript在1995年诞生了; 由Netscape公司,布兰登·艾奇(Brendan Eich)发明的ECMAScript客户端脚本语言; 主要应用在浏览器,在当时却不温不火. 直到后来Netscape与S

    2024年03月15日
    浏览(64)
  • 【C++系列P2】引用——背刺指针的神秘刺客(精讲一篇过!)

    前言 大家好吖,欢迎来到 YY 滴 C++系列 ,热烈欢迎! 如标题所示,本章主要内容主要来侃侃“ 引用 ”这个刺客!如下就是大纲啦~ 引用,即 取别名 。它的最大特点是编译器 不会为引用变量而开辟空间 ,他们 共用同一块 空间。   1. 引用使用时必须要 初始化 。 2. 引用在初

    2024年02月10日
    浏览(68)
  • 【C++系列P7】模板搞不懂?脑阔抖三抖!!精讲一篇过!

     前言 大家好吖,欢迎来到 YY 滴 C++系列 ,热烈欢迎! 本章主要内容面向接触过C++的老铁,主要内容含 目录 一.模板  1.函数模板 一.函数模板概念 二.函数模板的格式 三.函数模板的实例化  1.隐式实例化 2.显式实例化  3.模板参数的匹配原则  2.类模板 一.类模板的格式 二

    2024年02月08日
    浏览(41)
  • 数组(一篇带你掌握数组)

        在之前,我们想要存储一个数据的时候可以创建变量,例如存储一个整形的变量,我们使用int类型的变量来存储,那么如果存储一组相同类型的数据呢?这时我们就引入了 数组 的概念。 目录 一、一维数组的创建和初始化 1.1数组的创建 1.2 数组的初始化 1.3 一维数组的使

    2023年04月08日
    浏览(48)
  • 数据结构 队列(一篇基本掌握)

            任其事必图其效;欲责其效,必尽其方。——欧阳修;本篇文章主要写的是什么是队列、以及队列是由什么组成的和这些组成接口的代码实现过程。( 大多细节的实现过程以注释的方式展示请注意查看 )    话不多说安全带系好,发车啦 (建议电脑观看) 。 附

    2024年02月12日
    浏览(42)
  • 数据结构 栈(一篇基本掌握)

            时间就是生命,时间就是速度,时间就是气力。——郭沫若;本章继续学习数据结构,本章主要讲了什么是栈以及栈的基本功能和实现方法。    话不多说安全带系好,发车啦 (建议电脑观看) 。 附:红色,部分为重点部分;蓝颜色为需要记忆的部分(不是死记

    2024年02月12日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包