算法-时间频度& 时间复杂度& 空间复杂度

计算算法执行时间

  • 时间频度(Temporal Frequency): 一个算法所花费的时间与算法中语句的执行次数成正比. 算法中的语句执行次数称为语句频度或时间频度. 通过 T(n)来表示时间频度
  • 计算1-100的所有数字之和, 通过两种算法计算:

int total = 0;
int end = 100;

# 使用 for循环计算
for (i = 1; i <= end; i++) {
 total += i;
}
- 以上算法的时间频度是 T(n)=n+1

# 不通过循环, 而直接计算
total = (1 + end) * end / 2;
- 以上算法的时间频度是 T(n)=1

  • 随着 n的变大有3个特点(3种可以忽略的项):
  1. 常数项

(1) 2n+20和2n随着 n的变大, 执行时间间隔无限接近, 所以20可以忽略
(2) 3n+10和3n随着 n的变大, 执行时间间隔无限接近, 所以10可以忽略

  1. 低次项

(1) 2n2+3n+10和2n2随着 n的变大, 执行时间间隔无限接近, 所以可以忽略3n+10
(2) n2+5n+20和n2随着 n的变大, 执行时间间隔无限接近, 可以忽略5n+20

  1. 系数

(1) 随着 n的变大 5n^2+7n和 3n^2+2n的执行时间间隔无限接近, 此时可以忽略5和3
(2) 然而 n^3+5n和 6n^3+4n的执行时间间隔会分离, 可以发现多少次方是关键

时间复杂度(Time Complexity)

  • 算法执行速度, 主要看时间复杂度
  1. 一般情况下, 算法中的基本操作语句的重复执行次数是问题规模 n的某个函数, 用 T(n)表示, 若有某个辅助函数 f(n), 使得当 n趋近于无穷大时, T(n)/f(n)的极限值为不等于零的常数, 则称 f(n)是T(n)的同数量级函数. 记作 T(n)=O(f(n)), O(f(n))为算法的渐进时间复杂度, 简称时间复杂度
  • 例: T(n)=n2+7n+6与 T(n)=3n2+2n+2, 它们的 T(n)不同, 但时间复杂度相同. 都为 O(n2)
  • 计算方式:
    (1) 用常数1代替运行时间中的所有加法常数 T(n)=n2+7n+6 → T(n)=n2+7n+1
    (2) 修改后的运行次数函数中, 只保留最高阶项 T(n)=n2+7n+1 → T(n)=n2
    (3) 去除最高阶项的系数 T(n)=n2 → T(n)=n2 → O(n2)
  • 8种常见的时间复杂度, 由小到大依次为:
  1. 常数阶O(1)
  2. 对数阶O(log2n)
  3. 线性阶O(n)
  4. 线性对数阶O(nlog2n)
  5. 平方阶O(n^2)
  6. 立方阶O(n^3)
  7. k次方阶O(n^k)
  8. 指数阶O(2^n)
  • 随着 n的不断增大, 上述时间复杂度不断增大, 算法的执行效率会越来越低

常数阶O(1)

  • 无论是多少行代码, 只要是有循环等复杂结构, 那这个代码的时间复杂度就都为 O(1)

int a = 1;
int b = 2;
++a;
b++;
int c = a + b;

  • 上述代码在执行的时候, 所消耗的时间, 并不是随着某个变量的增长而增长, 所以无论这类代码有多少行, 都可以用 O(1)来表示时间复杂度

对数阶O(log2n)


int a = 1;
while (a < n) {
   a = a * 2;
}

  • 上述代码的 while每次循环都将 a乘以 2, 再将结果赋值给 a. 因此会越来越逼近 n大小. 假设循环 x次之后, a大于或等于 n了, 此时结束循环, 也就是2的 x次方等于 n(x=log2n), 也就是循环了 log2n次后结束循环
  • O(log2n)中2为底数, n为真数, 底数可以改变 若将2改为3 a = a * 3, 则可以标识为 O(log3n)

线性阶O(n)


for (int i = 1; i <= n; ++i) {
   i++;
}

  • 上述代码中 for循环内的代码会执行 n遍, 因此它消耗的时间是随着 n的变化而变化的, 因此这类代码都可以用 O(n)来表示

线性对数阶O(nlog2n)

  • 线性对数阶 O(nlogN)是将时间复杂度为 O(logn)的代码循环了 N遍的算法, 也就是线性阶和对数阶的嵌套 n*O(logN)

for (int m =1; m < n; m++) {
   i = 1;
   while (i < n) {
        i = i * 2;
   }
}

平方阶O(n2)

  • 平方阶就是嵌套了2层n循环, 也就是2层线性阶, 即 O(n²). 如果将其中一层循环的 n改成m, 那它的时间复杂度为 O(mn)

int m = 1;
for (int i = 1; i <= n; i++) {
   for(int j =1; j <= n;  j++) {
      m++;
   }
}

立方阶O(n^3)

  • 立方阶是嵌套了3层n循环

k次方阶O(n^k)

  • k次方阶是嵌套了 k层n循环

指数阶O(2^n)


public int abc(int n) {
    if (n <= 1) {
        return 1;
    } else {
        return abc(n - 1) + abc(n - 2);
    }
}

  • T(n)=T(n-1)+T(n-2)+1, 其中加1为算一次执行
  • T(n)=T(n-1)+T(n-2)是一个斐波那契数列(Fibonacci sequence, 黄金分割数列), 通过归纳证明法(Mathematical Induction, MI)可以证明. 即可以标识为 O((5/3)^n), 简化后为 O(2^n)

空间复杂度(Space Complexity)

  • 空间复杂度是一个算法在运行过程中临时所占用存储空间大小的量度, 有些算法是随着 n的变大, 随之占用更多的存储空间 如快速排序, 归并排序等
    * 目前趋势上, 算法主要关注的是时间复杂度. 还有市面上的 如 缓存系统, 基数排序算法等也都是空间换了时间, 提升了程序的执行速度

如果您觉得有帮助,欢迎点赞哦 ~ 谢谢!!

©️2020 CSDN 皮肤主题: 创作都市 设计师:CSDN官方博客 返回首页