【大O表示法】:究竟什么是时间复杂度?

187 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第8天,点击查看活动详情

1、写在前面

大家好,我是翼同学。今天文章的内容是:

  • 时间复杂度

2、内容

2.1、什么是时间复杂度?

所谓算法时间复杂度就是算法运行的时间度量,是其所求解问题规模nn的函数,我们不考虑具体的运行时间函数,只考虑时间函数的数量级,这称为算法的渐进时间复杂度,简称时间复杂度。记为O(f(n))O(f(n)),称作大OO表示法。

时间复杂度是一个函数,它定性描述该算法的运行时间

因此所谓的时间复杂度就是指最坏情况下,估算算法执行时间的一个上界。

2.2、大O表示法

OO表示法取时间函数f(n)f(n)的主项,而忽略f(n)f(n)中的常数和低次项。

OO表示法给出了时间复杂度的上界,作为算法的最坏情况运行时间的上界,即对任意数据输入的运行时间的上界。

举个例子:

假设某算法的时间函数为T(n)=(n+1)2=n2+2n+1T(n) = (n+1)^2 = n^2 + 2n + 1,此时让我们计算该算法的时间复杂度。利用大OO表示法中的简化思想:

  1. 去掉运行时间中的常数项:O(n2+2n)O(n^2 + 2n)
  2. 去掉常数系数:O(n2+n)O(n^2 + n)
  3. 只保留最高项:O(n2)O(n^2)

因此我们说,该算法的时间复杂度为O(n2)O(n^2)

小结:

算法的运行时间取决于原操作(核心操作)在算法中重复执行的次数,也就是语句频度。

2.3、常见的时间复杂度

常见的时间复杂度及其关系如下:

O(1)O(1)<O(log2n)O(log_2 n)<O(n)O(n)<O(nlogn)O(nlogn)<O(n2)O(n^2)<O(n3)O(n^3)<...<O(nk)O(n^k)<O(2n)O(2^n)...

简单举例如下:

(1) 常量阶

for(int i = 0; i<10000; ++i) {
    ++x;
    s+=x;
}

上述例子中,++x;的语句频度为10000,则其时间复杂度为O(1)O(1),即常量阶

(2) 对数阶

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

上述例子中,++x;的语句频度为log2nlog_2 n,则其时间复杂度为O(log2n)O(log_2 n),即对数阶

(3) 线性阶

for(int i = 1; i <= 2*n; ++i) {
    ++x;
    s+=x;
}

上述例子中,++x;的语句频度为2×n2×n,则其时间复杂度为O(n)O(n),即线性阶

(4) 线性对数阶

for(int i = 1; i <= n; i*=2) {
    for(int j = 1; j <= n; ++j) {
        ++x;
        s+=x;
    }
}

上述例子中,++x;的语句频度为nlog2nnlog_2n,则其时间复杂度为O(nlog2n)O(nlog_2n),即线性阶

(5) 平方阶

for(int i = 1; i <= n; ++i) {
    for(int j = 1; j < n/2; ++j) {
        ++x;
        s+=x;
    }
}

上述例子中,++x;的语句频度为n×n/2n×n/2,则其时间复杂度为O(n2)O(n^2),即平方阶

(6) 立方阶

for(int i = 1; i<=n; i++) {
    for(int j = 1; j<=n; j++) {
        c[i][j] = 0;
        for(int k = 1; k<=n; k++) {
            c[i][j] = c[i][j] + a[i][k] * b[k][j];
        }
    }
}

上述例子中,c[i][j] = c[i][j] + a[i][k] * b[k][j];的语句频度为n3n^3,则其时间复杂度为O(n3)O(n^3),即立方阶

2.4、空间复杂度

另外,记录一下空间复杂度。

一个算法所占用的存储空间包括三个方面:

  • 算法本身所占用的存储空间
  • 算法的输入输出所占用的存储空间
  • 算法在运行过程中临时占用的存储空间

类似算法的时间复杂度,我们采用空间复杂度来度量算法所需存储空间的多少。记为S(n)=O(f(n))S(n) = O(f(n)).

一般来说,算法执行时间的节省都是以增加空间存储为代价的。

另外,空间复杂度一般是考虑程序运行时占用内存的大小,而不是可执行文件的大小

举两个例子:

  • O(1)O(1)
int x = 0;
for (int i = 0; i < n; i++) {
    ++x;
}

在上述例子中,程序所需的内存空间并不会随着nn的变化而变化。因此该算法的空间复杂度为常量阶,记为O(1)O(1)

  • O(n)O(n)
int* p = new int(n);
for (int i = 0; i < n; i++) {
    p[i] = i;
}

在上述例子中,程序消耗的内存空间随着参数nn的增长而增长,呈线性增长,此时该算法的空间复杂度就记作O(n)O(n)


3、写在最后

好了,今天的笔记就到这里。