文章目录
1.最好情况时间复杂度(best case time complexity)
最好情况时间复杂度:在最理性的情况下,执行这段代码的时间复杂度
,比如下面的代码中,最好的情况,x为arr的第一个元素,则时间复杂度为O(1):
/**
* 最好:O(1)
* 最坏:n -> O(n)
* 平均: x的位置可能在 0,1,...n-1 以及 不在arr中, 共n+1种情况,每种情况的概率为 1/(n+1)<br>
* 因此,平均复杂度为: 1 * ( 1/(n+1) ) + 2 * ( 1/(n+1) ) + ... + n * ( 1/(n+1) ) + n *( 1/(n+1) ) = ( n(n+3) )/ ( 2* (n+1)
* -> O(n)
* @param arr
* @param n
* @param x
* @return
*/
public static int cal2(int [] arr, int n, int x) {
// 总 n -> O(n)?
int i = 0; // 1
int pos = -1; // 1
for(; i < n; ++i) { // n?
if(arr[i] == x) { // n?
pos = i; // n?
break;
}
}
return pos;
}
2.最坏情况时间复杂度(worst case time complexity)
最坏情况复杂度,就是在最糟糕的情况下,执行这段代码的时间复杂度
,如下的代码中,如果arr中无x, 则需要把整个arr都遍历一遍,复杂度为O(n):
/**
* 最好:O(1)
* 最坏:n -> O(n)
* 平均: x的位置可能在 0,1,...n-1 以及 不在arr中, 共n+1种情况,每种情况的概率为 1/(n+1)<br>
* 因此,平均复杂度为: 1 * ( 1/(n+1) ) + 2 * ( 1/(n+1) ) + ... + n * ( 1/(n+1) ) + n *( 1/(n+1) ) = ( n(n+3) )/ ( 2* (n+1)
* -> O(n)
* @param arr
* @param n
* @param x
* @return
*/
public static int cal2(int [] arr, int n, int x) {
// 总 n -> O(n)?
int i = 0; // 1
int pos = -1; // 1
for(; i < n; ++i) { // n?
if(arr[i] == x) { // n?
pos = i; // n?
break;
}
}
return pos;
}
3.平均情况时间复杂度(average case time complexity)
最好、最坏时间复杂度其实都是极端情况下的代码复杂度,发生的概率并不大,具有普适性的还是平均时间负责度
,
-
粗略平均:
x的位置可能在 0,1,...n-1 以及 不在arr中, 共n+1种情况,累加再除以n+1得平均时间: (1+2+3+...+n+n)/ (n+1) = ( n(n+3) )/ ( 2* (n+1) -> O(n)
-
精准平均:运用概率统计,
精准平均: x的位置可能在 0,1,...n-1 以及 不在arr中, 共n+1种情况,每种情况的概率为 1/2,而数组中每个元素的概率为1/n: 因此,平均复杂度为: 1*(1/2)*(1/n) + 2*(1/2)*(1/n) + 3*(1/2)*(1/n) + ... + n*(1/2)(1/n) + n*(1/2) = (3n+1)/4 -> O(n)
这个值就是概率论中的加权平均值,也叫做期望值,所以平均时间复杂度的全称应该叫做加权平均时间复杂度 或者 期望时间复杂度
/** * 最好:O(1) * 最坏:n -> O(n) * 粗略平均: x的位置可能在 0,1,...n-1 以及 不在arr中, 共n+1种情况,累加再除以n+1得平均时间:<br> * ( 1+2+3+...+n+n)/ (n+1) = ( n(n+3) )/ ( 2* (n+1) * -> O(n) * 精准平均: x的位置可能在 0,1,...n-1 以及 不在arr中, 共n+1种情况,每种情况的概率为 1/2,而数组中每个元素的概率为1/n<br> * 因此,平均复杂度为: 1*(1/2)*(1/n) + 2*(1/2)*(1/n) + 3*(1/2)*(1/n) + ... + n*(1/2)*(1/n) + n*(1/2) = (3n+1)/4 * -> O(n) * @param arr * @param n * @param x * @return */ public static int cal2(int [] arr, int n, int x) { // 总 n -> O(n)? int i = 0; // 1 int pos = -1; // 1 for(; i < n; ++i) { // n? if(arr[i] == x) { // n? pos = i; // n? break; } } return pos; }
4.均摊时间复杂度(amortized time complexity)
/**
* 最好:只一次 O(1)
* 最差: n+n -> 2n -> O(n)
* 平均: 1*( 1/(n+1) ) + 1*( 1/(n+1) ) + ...+ n * ( 1/(n+1) ) = n/2n+2 -> O(1)
* 均摊: O(1): 前n次 都为O(1), 每隔n+1次,复杂度为O(n), 把这n次操作均摊到<br>
* 前面的n次,总的复杂度为 O(1).
* @param val
* @param n
*/
public static void insert(int val, int n) {
int[] arr = new int[n];
int count = 0;
if(count == arr.length) { // 1
int sum = 0; // 1
for(int i=0; i < arr.length; i++) { // count == arr.length ? n : 0
sum = sum + arr[i]; // count == arr.length ? n : 0
}
arr[0] = sum; // count == arr.length ? 1 : 0
count = 1; // count == arr.length ? 1 : 0
}
arr[count] = val; // 1
++count; // 1
}
均摊其实是平均的一种特殊情况,特点:
对于 insert() 函数来说,O(1) 时间复杂度的插入和 O(n) 时间复杂度的插入,出现的频率是非常有规律的,而且有一定的前后时序关系,一般都是一个 O(n) 插入之后,紧跟着 n-1 个 O(1) 的插入操作,循环往复。
每一次 O(n) 的插入操作,都会跟着 n-1 次 O(1) 的插入操作,所以把耗时多的那次操作均摊到接下来的 n-1 次耗时少的操作上,均摊下来,这一组连续的操作的均摊时间复杂度就是 O(1)