时间复杂度
在算法中,所谓的时间复杂度可以简单的理解成为算法的运行时间。接下来我们可以简单的去看一下时间复杂度是怎么计算的
例子:
javascript
function print1() {
console.log('打印')
}
// 函数内代码只执行了1次
function print2(n) {
for (let i = 0; i < n; i++) {
console.log('打印')
}
}
第一个 print1 很明显的看出来只执行了1次
第二个函数:对于print2 我们可以先假设 **n = 2
**
回到 n 这个数上,我们再计算一下执行了几次
可以得出结果就是 一共执行了 3n + 2次
结论
通常一段代码的总执行次数,我们会用T(n)来表示, 而如果n的数据比较大时,T(n)来表达就不是很合适。在算法中,我们通常会用T(n)的简化估算值 来计算代码的运行速度,而这个估算值就被称为时间复杂度。
T(n) --> 时间复杂度
- print1 = T(n) = 1 --> 对于T(n)是个常数时,可以直接将其估算成 1 --> O(1)
- print2 = T(n) = 3n + 2 --> 如果n足够大的时候,可以理解成忽略其常数和n前面的系数,也就n --> O(n)
- 补充: T(n) = 4n^8 + 10n^7 + 2 -->同样的道理,n足够大时,取n的次方最大作为复杂度--> O(n^8)
常见的例子
例子1:
js
function print1() {
console.log('打印1') // 打印了一次
console.log('打印2') // 打印了一次
console.log('打印3') // 打印了一次
console.log('打印4') // 打印了一次
console.log('打印5') // 打印了一次
}
// 复杂度 O(1)
例子2:
js
function print2(n) {
for (let i = 0; i < n; i++) {
console.log('打印') // 打印了一次
}
}
// 复杂度 O(n)
可以简单总结出来:代码存在多少层循环,时间复杂度就是多少。
例子3:
js
function print3(n) {
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
console.log('打印') // 打印了一次
}
}
for (let k = 0; k < n; k++) {
console.log('打印') // 打印了一次
}
}
// 当 i = 0,内部会执行 n + 1 次, i = 1, 持续 2n + 2,依次类推,应该会执行个 n*n + n = n^2+n 次。
// 复杂度为O(n^2)
例子4:
js
function print4(n) {
if (n > 100) {
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
console.log('打印') // 打印了一次
}
}
} else {
for (let i = 0; i < n; i++) {
console.log('打印') // 打印了一次
}
}
}
// 如果是存在判断条件,则还是以最长运行时间最为最终结果
// 复杂度为O(n^2)
例子5:
js
function print5(n) {
for (let i = 0; i < n; i++) {
for (let j = i; j < n; j++) {
console.log('打印') // 打印了一次
}
}
}
// i = 0 -> n
// i = 1 -> n - 1
// ...
// i = n - i -> 1
// T(n) = n + (n - 1) + (n - 2) + .... + 2 + 1 = n(n + 1) / 2 = 1/2 * n^2 + 1/2*n
// 复杂度为O(n^2)
例子6:
js
function print6(n) {
for (let i = 0; i < n; i * 2) {
console.log('打印') // 打印了一次
}
}
// 假设 n = 8
// T(n) = 3 --> 2^3 = 8 --> log(2)(8) = 3(指数和对数的转换)
// 复杂度可以简单表示成 O(logn) -->(当n足够大时,可以忽略其底数,只保留其真数和对数)
可以补充一下简单的数学知识
两者可以相互转换,转换公式为a^x=N <=> log(a)(N) = x
例子7:
js
function print(n) {
for(let j = 0; j < n; j++) {
for (let i = 0; i < n; i * 2) {
console.log('打印') // 打印了一次
}
}
}
// 由例子6可以简单得出:
// 复杂度为O(nlog(n))
空间复杂度
空间复杂度可以简单理解成一个算法在运行过程中占用存储空间大小 的度量。通常来说,计算一个算法的空间复杂度公式为:S(n) = O(fn(n)) 其中n 为问题的规模,fn为算法所占用空间大小的函数
例子:
js
function print(n) {
let a = []
const b = 1 // 是个常量 -> 1
for(let i = 0; i < n; i++) {
a.push(i); // a的大小随的n变而变大
}
}
// 我们可以简单的估量程序所占用 a占用了n个空间,b暂用了一个,一共为n+1 --> O(n)
常见的几种复杂度:
- 如果申请的是有限个数(常量)的变量(变量空间 ),空间复杂度为 O(1) 。
- 如果申请的是一维数组,队列或者链表(线性空间 )等,那么空间复杂度为 O(n) 。
- 如果申请的是二维数组(二维空间 ),那么空间复杂度为 O(n²) 。
- 如存在递归则比较特殊(递归空间 ),空间复杂度为O(n) (递归存在时,计算机会专门分配一块空间,来存储"方法调用栈",当进入更深一层函数时,会进行入栈操作,执行完会进行出栈操作, 因此递归所需要的内存空间跟递归的深度成正比。)
- 如果是在循环体中申请的数组等,可能就需要取嵌套的乘积来作为空间复杂度,这种就需要具体的进一步分析。