浅谈时间复杂度

时间复杂度

定义

衡量一个算法的快慢,一定要考虑数据规模的大小。所谓数据规模,一般指输入的数字个数、输入中给出的图的点数与边数等等。一般来说,数据规模越大,算法的用时就越长。而在算法竞赛中,我们衡量一个算法的效率时,最重要的不是看它在某个数据规模下的用时,而是看它的用时随数据规模而增长的趋势,即 时间复杂度

​ ——OI Wiki

在算法中,使用时间复杂度来衡量程序运行时间的多少。每条语句执行的次数称为该语句的频度,整段代码的总执行次数则称为整段代码的频度。在算法竞赛中,由于输入可以在给定的数据范围内任意给定,我们为保证算法能够通过某个数据范围内的任何数据,一般考虑最坏时间复杂度。

下面,我将用更通俗易懂的语言,解释时间复杂度。

分析

在推导的时候,我们应该采用无限大的思想来简化大 \(O\) 表达式,具体如下:

1.用常数 \(1\) 代替运行时间中的所有加法的常数。无论常数是多少都使用 \(1\) 替换。因为执行函数和问题规模 \(n\) 的大小无关,它是执行时间恒定的,像时间复杂度为\(O(1)\) 的又被称作常数阶。如:执行 \(100\)\(1 \to n\) 的循环,这份代码的时间复杂度为 \(O(n)\) 而并非 \(O(100n)\)

2.如果最高阶项存在且系数不为 \(1\),则去除掉与这个项相乘的系数,例如 \(O(2n^2)\) 系数为 \(2\),直接简化为 \(O(n^2)\)

3.如果表达式有多项含有无限大变量的式子,只保留一个拥有指数最高的变量的式子。如 \(O(2n^2+2n)\) 应简化为 \(O(n^2)\)

经过上面三个步骤推到出来的结果就是算法对应的大 \(O\) 阶。

注意,在遇到类似于 \(O(2n+m)\) 的时间复杂度时,由于 \(n\)\(m\) 之间没有联系,所以这份代码的时间复杂度应是 \(O(n+m)\)。在遇到类似于 \(O(n^2+nq)\) 的时间复杂度时,由于我们无法确定 \(n^2\)\(nq\) 的大小(\(n\)\(q\) 的大小不确定),因此这份代码的时间复杂度依然是 \(O(n^2+nq)\)

举例:

  • \(O(12)\to O(1)\)

  • \(O(2n+3)\to O(n)\)

  • \(O(3n^2+2n+1)\to O(n^2)\)

  • \(O(5\log_2 n+20)\to O(\log n)\)

  • \(O(2n+3n\log_2 n+19)\to O(n\log n)\)

  • \(O(6n^3+2n^2+3n+4)\to O(n^3)\)

  • \(O(2^n+n^4)\to O(2^n)\)

Another

哪些数是常数?

我们曾提到,“用常数 \(1\) 代替运行时间中的所有加法的常数”,那么,什么是常数呢?

我们一个例子看起。

const int n=114514;
for (int i=1;i<=n;i++){
    cout<<"hello world\n";
}

代码中,\(MAXN\) 虽然是一个变量,但是它的值是固定的,所以它不被看做输入数据规模,所以代码的时间复杂度是 \(O(1)\) 而不是 \(O(n)\)

进行时间复杂度计算时,哪些变量被视作输入规模是很重要的,而所有和输入规模无关的量都被视作常量,计算复杂度时可当作 \(1\) 来处理。

posted @ 2024-07-16 21:15  shimingxin1007  阅读(12)  评论(0编辑  收藏  举报