《算法问题实战策略》-chaper17-部分和
数组上的一个基本优化——部分和:
对于一定长度的数组,我们想不断访问这个数组上的某个区间的和,我们能够怎么做呢?这里先不去谈一些数据结构在这个问题上的优化处理。首先我们最简单的一个方法就是穷举出所有区间段然后求值保存起来,但是O(n^2)的时间复杂度并没有太多的实际应用的意义。
这里考虑一个非常简单但是常用的优化方法——部分和。
对于一个数组a[],我们在输入数据的时候,顺便记录数组部分和psum[],而对于部分和数组psum[]的定义如下:
psum[i] = ∑a[j] , j∈[1,j].
基于部分和数组,能够看到,我们在多次访问数组某个区段的和的时候,可以通过O(1)的时间复杂度得到,即a[i] + a[i+1]+…a[j] = psum[j] – psum[i].
结合一个具体的题目我们来尝试一下这个方法:
Q:假设有以正数和负数组成的数组A[],其数组中区间之和最接近0的区间。
考虑从部分和的角度去优化处理这个问题,我们首先得到这个数组的部分和,但是这里需要用结构体记录一下psum[]当前的下标。然后我们回到一开始的问题,区兼职和最接近零,也就是min{|psum[i]-psum[j]| | i、j∈[1,n]},我们将psum[]数组进行排序,然后在线性时间复杂度O(n)下维护一个psum[i+1]-psum[i]的最小值即可,两个结构体元素存着的下标变量即是原来数组对应的区段。
综合起来,整体的时间复杂度是O(nlog n) + O(n),相比O(n^2)已经优化很多了。
在c++STL中有直接进行部分和的函数,简单的用法如下:
#include <iostream> #include <functional> #include <numeric> using namespace std; int main () { int val[] = {1,2,3,4,5}; int result[5]; partial_sum (val, val + 5, result);//用法:partial_sum(<数组起点>,<数组终点>,记录部分和的数组) cout << "using default partial_sum: "; for (int i=0; i < 5; i++) cout << result[i] << ' '; return 0; }