Time Complexity - 时间复杂度
简介
狭义的说,算法是利用计算机软件解决数学问题的方法。对于具有确定数学模型的问题,将问题数据输入算法中就可以得到该问题的解。简单的问题比如求两个位于之间的整数之和,这个问题的输入数据是两个整数和,解是整数,其中。复杂的问题比如求拥有个节点网络流的最大流,输入数据是一个网络流,解是该网络流的最大流。
时间复杂度是衡量算法性能的度量。如果把算法比作数学中的函数,那么时间复杂度就是运行该函数所消耗的时间、空间(内存)。一般将简单的数学计算、读写变量看作基本操作,其消耗的时间看作基本时间单位。基本操作只是一种抽象的认知,并不是死板规定。比如在计算两个32位整数的问题上,考虑下面两种算法:
(1) 累加模拟法:重复个累加,或个累加。该算法把一次相加看作一个基本操作,则其所需时间至多为个时间单位;
(2) Booth’s Multiplication Algorithm:利用位操作计算两整数相乘。假设,那么第种算法需要次累加操作,而该算法只需要次位操作,而且CPU的位操作速度极快,一次位操作所需的真正时间远小于一次整数加法操作;
在上面两种算法中,认为加法操作是第个算法中的基本操作,位操作是第个算法中的基本操作。虽然实际的x86或amd64架构CPU上加法操作和位操作的性能相差甚远,但在算法中都抽象的认为它们都是基本操作,忽略其细节的差别。我们更关注第个算法通过利用整数的比特位为,远小于整数本身,从而将运算次数从次降低到了次带来的提高。
渐进记号
不同问题的数据规模是不一样的,求两整数之和的问题规模是两个整数,即;求拥有个节点的网络流的最大流的问题规模则是。常用渐进记号表示一个问题的规模,比如表示问题规模是常数的,表示问题规模是线性的。
不同算法消耗的时间、空间也不一样,用渐进记号来描述算法的时间复杂度或空间复杂度。常见的时间复杂度有:
(1) 常数时间:,不论问题规模,算法都能在一个固定时间内解决问题,这里的并非特指1次操作,而是指常数数量操作,因此也不存在这样的复杂度。比如上文中的两整数相加;
(2) 对数时间:、 n O(n) n O(n^2) n n O(n^3) O(n!) n O(n^3) \gt O(n^2) \gt O(n \cdot log_2 n) \gt O(n) \gt O(log_2 n) \gt O(1) O(1) z = a + b \times c \div (2 * d + e) O(1) n^2 + log_2 n + 3 \times 4 O(n^2) + O(log_2 n) + O(1) O(n^2) O(1) 2 \cdot n O(2 \cdot n) O(n) c = a + b T(1) = T(1) + T(1) = O(1) T(1) O(1) O(1) n T(n) = \begin{cases} 1 & n = 1 \ 2 \cdot T(\frac{n}{2}) + O(n) & n \gt 1 \end{cases} O(n) T(n) T(\frac{n}{2}) O(n) \begin{matrix} T(n) & = & 2 \cdot T(\frac{n}{2}) + n & & \ & = & 2 \cdot T(2 \cdot T(\frac{n}{2^2}) + \frac{n}{2}) + n & = & 2^2 \cdot T(\frac{n}{2^2}) + 2 \cdot n \ & = & 2 \cdot T(2 \cdot T(\frac{n}{2^3}) + \frac{n}{2^2}) + 2 \cdot n & = & 2^3 \cdot T(\frac{n}{2^3}) + 3 \cdot n \ & = & \cdots & & \end{matrix} L T(\frac{n}{2^L}) = 1 \frac{n}{2^L} = 1 L = log_2 n \begin{matrix} T(n) & = & 2^L \cdot T(\frac{n}{2^L}) + L \cdot n \ & = & 2^{log_2 n} + n \cdot log_2 n \ & = & n + n \cdot log_2 n \ & = & O(n \cdot log_2 n) \end{matrix} O(n \cdot log_2 n) $$。