势能分析法 学习笔记
势能分析法
概述
在进行部分操作时,我们操作的复杂度不一定符合题目要求(额外操作);但是如果 它的总复杂度 是符合要求的,那么这个操作也可以使用。
这种分析方法(思想)成为 势能分析法
分析
在进行势能分析时,关键是找到 总复杂度,其次是论证总复杂度是随着额外操作的进行而严格递减的。
为了找到总复杂度,我们可以尝试分析 每一部分代码的复杂度和调用次数
例子
典例1:线段树合并
inline int merge(int x,int y){
if(!x || !y) return x+y;
sum[x]+=sum[y];
ls[x]=merge(ls[x],ls[y]);
rs[x]=merge(rs[x],rs[y]);
return x;
}
当 $ x \leq logN $ 且 $ y \leq N $ 时,且进行 \(N\) 次操作,总复杂度是\(O(nlogn)\)级别的。
首先除去递归部分之外,时间复杂度主要消耗在第一行和第二行。
我们逐个分析:
-
对于第一行,只有在两个节点中的一个点是空节点时,我们才会运行,因此运行次数等于空儿子个数。有二叉树的性质,我们可以得知,这个数值等于 \(\text{叶子个数}+\text{只有一个儿子的节点数}\) 由 \(x\) 和 \(y\) 的约定可以得知,在每一次操作中,这部分的时间复杂度不超过 \(O(logn)\)。
-
对于第二行,每一次运行都会时总节点个数减一,而总节点个数是一定的,因此这部分时间复杂度不超过 \(O(nlogn)\)。
综上这部分代码的总复杂度是 \(O(n*logn+nlogn)=O(nlogn)\) 的。
总结
只要找到时间复杂度开销大的地方,进行分析,找到总量,明确递减,就说明算法可行。