先识别重复计算
假设你拿到一个长度为5的数组,元素依次为2、3、5、1、4。若反复询问“第2个元素到第3个元素的和”,最直接的做法是每次从第2个位置开始累加3和5,得到8;下一次问“第3个到第5个”,又从头累加5、1、4。这种每问一次就重新加一遍的做法可以看作暴力搜索:它逻辑简单,但同样的加法被重复执行多次。数组越长、询问越多,重复劳动越明显。
用前缀和保存部分结果
优化的思路是:把已经算过的部分结果存下来,避免每次重复。定义前缀和数组P,P[0]=0,P[i]表示原数组前i个元素的总和。上面数组的P为:P[1]=2,P[2]=5,P[3]=10,P[4]=11,P[5]=15。对于下标从1开始的闭区间[l,r],区间和等于P[r]-P[l-1]。例如区间[2,3]的和为P[3]-P[1]=10-2=8,与直接累加一致。查询时只做一次减法,不再遍历区间。
预处理的前提:数据稳定
前缀和能提速,前提是数组在预处理后不频繁修改。一旦修改某个元素,从该位置起的所有前缀和都要同步更新。例如把第3个元素从5改成7,那么P[3]、P[4]、P[5]都要重新计算。因此,若题目是“数组固定、只做查询”,前缀和非常合适;若需要“边修改边查询”,则应考虑树状数组或线段树等结构。选择算法时,要权衡预处理代价与查询代价。
把“数重复”当作优化起点
拿到题目先别急着写循环,可以问自己:程序里有哪些重复计算?重复部分能否提前算好?提前算的代价是否值得?
自拟一个练习:数组[1,3,2,4],求所有长度为2的连续子数组的和。暴力做法是分别计算[1,3]=4、[3,2]=5、[2,4]=6。用前缀和,P[0]=0,P[1]=1,P[2]=4,P[3]=6,P[4]=10;区间[i-1,i]的和用P[i]-P[i-2]依次得到4、5、6。虽然小数据看不出优势,但设想数组长度10万、询问10万次,若每次询问都覆盖长度为10万的整个数组,暴力累加在最坏情况下约100亿次加法;前缀和预处理需10万次加法,每次查询一次减法,合计约20万次操作。这种数量级差距正是优化的意义。



