我们经常把时间复杂度作为算法效率的指标之一。那么我们怎么去计算一个算法的时间复杂度呢?
计算原则
首先,算法时间复杂度并不是指算法的总执行时间,而是基本操作的总次数。用一句话来概括:算法中基本操作的执行次数可以作为算法时间复杂度的度量。
因此:对一个算法的复杂度分析有以下的几个步骤:
- 确定算法中的基本操作以及问题的规模
- 根据基本操作执行情况确定执行次数
- 利用大O表示法,省略系数、低阶、常量,具体就是找出中变化最快的项作为时间复杂度的度量
从上面我们可以得到下面结论:
- 与无关时,
- 与是线性关系时,
- 与是二次关系时,
- 以此类推
并且根据数学性质,我们还有以下结论:
上面说的比较抽象,我们来用demo来学习一下如何计算
案例
案例1
function run(n){
let i = 0,j=100;
while(i<n){
++j;
i+=2;
}
}
分析:
- 找出基本规模,确定规模
- 因为依赖于内层的
while
循环,所以++j
,i+=2
为基本操作 - 循环条件
i<n
,所以规模就是参数n
- 因为依赖于内层的
- 确定执行次数
- 循环的开始与结束与
i
有关,i
每次自增2
,假设自增m
次后循环结束,最后,(K为修正n和m的接近程度的常数) - 最后,得到,即
- 循环的开始与结束与
- 利用大O表示法,省略系数、低阶、常量
- 很显然,是增长最快的项,去除系数
- 得到
案例2
冒泡排序(简单版)
function run(){
let arr = [3,4,1,2];
for (let j = 0; j < arr.length - 1; j++) {
// 这里要根据外层for循环的 j,逐渐减少内层 for循环的次数
for (let i = 0; i < arr.length - 1 - j; i++) {
if (arr[i] > arr[i + 1]) {
let temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
}
}
}
}
分析:
- 找出基本规模,确定规模
- 因为依赖于内层的循环,所以
let temp = arr[i]
,arr[i] = arr[i + 1]
,arr[i + 1] = temp
为基本操作 - 循环条件
arr
的length
,所以规模就是arr.length
- 因为依赖于内层的循环,所以
- 确定执行次数
- 外层必定循环次,内层因为
if
的判断条件,分为两种情况 - 最好的情况(全部正序):
- 最坏的情况(全部倒序):
- 外层必定循环次,内层因为
- 利用大O表示法,省略系数、低阶、常量
- 因此,最好的情况是,最坏的情况是
- 利用加权平均值得平均时间复杂度(不懂的话可以看看概率论)
案例三
function run(n){
let i = 0,s = 0;
while(s<n){
++i;
s=s+i;
}
}
- 找出基本规模,确定规模
- 因为依赖于内层的
while
循环,所以++i
,s=s+i
为基本操作 - 循环条件
s<n
,所以规模就是参数n
- 因为依赖于内层的
- 确定执行次数
- 循环的开始与结束与
s
,i
有关,假设自增m
次后循环结束,因此,,,... , = ,因此 - 最后,得到,即
- 循环的开始与结束与
- 利用大O表示法,省略系数、低阶、常量
- 得到
到了,到此,我相信各位同学已经知道了计算时间复杂度的方式了,各位同学如果还有兴趣的话,可以自己去把排序算法都计算一下,可以加深一下自己的认知。
参考文档
- 《天勤数据结构》