Backto Algo Index
上一篇说到了 Big-O 分析法, 但是那只适合分析复杂度随着 n n n 增大的趋势, 实际生产中, 我们还需要知道更细节的指标, 比如
先上代码
// n 表示数组 array 的长度
int find(int[] array, int n, int x) {
int i = 0;
int pos = -1;
for(; i < n; ++i) {
if(array[i] == x) {
pos = i;
break;
}
}
return pos;
}
很简单的, 在一个数组中 find 一个数是否存在, 逐一分析,
x = array[0]
, 一击即中, 复杂度 O ( 1 ) O(1) O(1)x = array[n-1]
, 或者 x not in array
, 那么需要找 n n n 次, 复杂度 O ( n ) O(n) O(n)不同与前面三个通用的简单的case, 均摊复杂度分析对应的是一种特殊的case
如果一个复杂度很高的操作, 会导致后面一连串复杂度很低的操作, 那么我们就可以把第一个操作的高复杂度均摊到后面的低复杂度上, 进而分析整体的复杂度. 这一类中最好的例子就是 insert
int[] arry = new int[n];
int count = 0;
void insert(int val) {
if(count == array.length) {
int sum = 0;
for(int i=0; i < array.length; ++i)
sum += array[i]
array[0] = sum;
count = 1;
}
array[count] = val;
++count;
}
这段代码的意思是向一个数组中 insert 数字, 如果有空位则直接插入, 是 O ( 1 ) O(1) O(1), 如果没有空位, 则把所有已有的数字求和置于数组首位, 再从第二个位置开始插入, 求和这波操作的复杂度就是 O ( n ) O(n) O(n), 如果我们用平均算一下, 就是 1 + 1 + 1 + . . . + 1 + n n + 1 = 2 n n + 1 \frac {1 + 1 + 1 + ... + 1 + n}{n+1} = \frac{2n}{n+1} n+11+1+1+...+1+n=n+12n, 用 Big-O 记做 O ( 1 ) O(1) O(1).
观察发现, 每一次数组满员, 触发 1 次复杂度为 n n n 的操作之后, 都会紧跟着 n − 1 n - 1 n−1 个复杂度为 1 1 1 的操作, 我们视这一组操作为一个原子操作, 均摊复杂度, 就是 O ( 1 ) O(1) O(1). 可见均摊就是加权平均的一个特例, 这里单拎出来,也是因为这个操作比较特殊, 也比较常见, 比如除了上面的 insert
还有 add
, C++ vector 中push数据, 如果push 满了, 就新开辟一个2倍的空间出来, 把原来的值copy过去, 这一波是 O ( n ) O(n) O(n), 之前和之后的一个一个插, 都是 O ( 1 ) O(1) O(1), 平均下来复杂度就是 O ( 1 ) O(1) O(1). 由此也可以联想到, 虽然 vector 是变长的, 但是能给定大小的情况下还是要给定capacity, 这样像 add
这样的操作就完全变成了 O ( 1 ) O(1) O(1).