【数组】前缀和
前缀和
给出一个数列:
1 2 3 4 5 6 7 8 9
它的前缀和:
1 3 6 10 15 21 28 36 45
前缀和即:从第一个元素到该元素之和
通常我们会在数组中触及到这类知识。
假设给出原数组 a[5] = {1,2,3,4,5},我们可以得到前缀和数组,假设为S[5] = {1,3,6,10,15}。
那么可以得到: S[n] = S[n-1] + a[n],即求前缀和的公式。
那么前缀和数组有什么用呢?#
以下给出一道经典前缀和问题:
(题目出自acwing,侵删)
首先我们先从朴素思想开始考虑,求m次区间和的时间复杂度是O(mn2)
while(m--) { int l , r; cin >> l >> r; int sum = 0; for(int i = l; i <= r; i++) { sum += num[i];(假设下标从1开始) } cout << sum << endl; }
我们会发现,我们没求一次区间和,就要循环一次,那么有没有一种方法可以让O(n)的求和变为O(1)的时间复杂度呢?
答案就是使用前缀和。对区间[l,r],通过对求前缀和过程的分析可以的出,区间和为s[r]-s[l-1]。因此我们可以通过一次运算求出原数组任意一段数据的和。
以下为代码:
点击查看代码
#include<iostream> #define N 100010 using namespace std; int nums[N]; int s[N]; int main() { int n,m; cin >> n >> m; for(int i = 0; i < n; i++) { cin >> nums[i+1]; s[i+1] = s[i] + nums[i+1];//求前缀和 } while(m--) { int l,r; cin >> l >> r; cout << s[r]-s[l-1] << endl;// } return 0; }
题目#
前缀和结合哈希表#
https://leetcode.cn/problems/subarray-sum-equals-k/
在数组中: 1.子数组 子数组的定义:一个或连续多个数组中的元素组成一个子数组(子数组最少包含一个元素) 2.子序列 子序列的定义:子序列就是在原来序列中找出一部分组成的序列(子序列不一定连续)
如果使用for嵌套循环暴力枚举O(n2)的方法必然会超时。
那么分析题意,可知求区间和为k的区间个数,即 s[i] - s[j] == k
同样,如果对前缀和数组进行枚举同样是O(n2)的时间复杂度,会超时。
我们用哈希表存储前缀和数组,结合公式s[i] - s[j] == k的变形s[j] = s[i] - k就可以实现O(n)的时间复杂度的解法。
即:对每一个前缀和数组元素s[i],利用哈希表查找s[i] - k是否存在,存在则找到了一个或n个符合题意的区间。
以下为代码
点击查看代码
class Solution { public: int subarraySum(vector<int>& nums, int k) { unordered_map<int, int> mp; mp[0] = 1; int sum = 0; int ans = 0; for(int i = 0; i < nums.size(); i++) { sum += nums[i]; if(mp.find(sum - k) != mp.end())// ans += mp[sum - k]; mp[sum]++; } return ans; } };
前缀乘积#
https://leetcode.cn/problems/subarray-sum-equals-k/
最先想到的是遍历求乘积,也就是O(n2)的暴力算法,但这不符合题目的要求。
那么我们需要对算法进行优化,从朴素思想出发,遍历求乘积时,从0到n-1累乘,中间跳过i,那么天然地由i分为左右两个区间,根据乘法的结合律,得出answer[i] = i左侧元素的乘积*i右侧元素的乘积。
那么我们只需要提前将每一个i左侧、右侧元素的乘积算出来,就可以实现O(n)时间复杂度的算法。
点击查看代码
class Solution { public: vector<int> productExceptSelf(vector<int>& nums) { int n = nums.size(); vector<int> answer(n); vector<int> l(n); vector<int> r(n); l[0] = 1; r[n - 1] = 1; for(int i = 1; i < n; i++) l[i] = l[i-1]*nums[i-1]; for(int i = n - 2; i >= 0; i--) r[i] = r[i+1]*nums[i+1]; for(int i = 0; i < n; i++) answer[i] = l[i]*r[i]; return answer; } };
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律