算法复杂度分析——时间复杂度(上)

1为什么要计算时间复杂度

一个算法的评价主要从时间复杂度空间复杂度来考虑。

2时间复杂度概念

2.1时间频度T(n)

一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间多,哪个算法花费的时间少就可以了。并且一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)。算法的时间复杂度是指执行算法所需要的计算工作量。

2.2时间复杂度

在刚才提到的时间频度中,n称为问题的规模,当n不断变化时,时间频度T(n)也会不断变化。但有时我们想知道它变化时呈现什么规律。为此,我们引入时间复杂度概念。

一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),存在一个正常数c使得 f ( n ) ∗ c > = T ( n ) f(n)*c>=T(n) f(n)c>=T(n)恒成立。记作 T ( n ) = O ( f ( n ) ) T(n)=O(f(n)) T(n)=O(f(n)),称 O ( f ( n ) ) O(f(n)) O(f(n))为算法的渐进时间复杂度,简称时间复杂度。

T ( n ) = O ( f ( n ) ) T(n) = O(f(n)) T(n)=O(f(n))

T(n) 我们已经讲过了,它表示代码执行的时间;n 表示数据规模的大小;f(n) 表示每行代码执行的次数总和。因为这是一个公式,所以用 f(n) 来表示。公式中的 O,表示代码的执行时间 T(n) 与 f(n) 表达式成正比。

常见的时间复杂度如下
在这里插入图片描述

3时间复杂度分析

分析时间复杂度的技巧

  • 只关注循环次数最多的一段代码
    我刚才说了,大 O 这种复杂度表示方法只是表示一种变化趋势。我们通常会忽略掉公式中的常量、低阶、系数,只需要记录一个最大阶的量级就可以了。所以,我们在分析一个算法、一段代码的时间复杂度的时候,也只关注循环执行次数最多的那一段代码就可以了。这段核心代码执行次数的 n 的量级,就是整段要分析代码的时间复杂度。
  • 加法法则:总复杂度等于量级最大的那段代码的复杂度
    上下分段代码,非嵌套的复杂度
  • 乘法法则:嵌套代码的复杂度等于嵌套内外代码代码复杂度的乘积
    其实就是嵌套循环

3.1示例一

如下程序
在这里插入图片描述
时间频度分析
红色框各需1个时间单元,共3个时间单元
蓝色框各需n个时间单元,共 2 × n 2 \times n 2×n个时间单元
绿色框所需n+ 1个时间单元
一共花费了 T ( n ) = 3 + 2 n + n + 1 = 3 n + 4 T(n) =3 + 2n + n + 1=3n + 4 T(n)=3+2n+n+1=3n+4
我们一般只关心随着问题规模n趋于无穷时函数中对函数结果影响最大的项,也就是最高次项,从而忽略低阶项.
n n n非常大时,即可以忽略常数项和高次项的系数,所以时间复杂度为 O ( n ) O(n) O(n)
综上,可以看最内层执行次数最多的代码。

3.2示例二

如下程序
在这里插入图片描述
直接看内层语句执行多少次,即终止循环的条件。先假设执行 x x x次,则满足 2 x = n 2^x = n 2x=n,解得 x = log ⁡ ( n ) x = \log(n) x=log(n)
所以时间复杂度为 O ( log ⁡ ( n ) ) O(\log(n)) O(log(n))

4汉若塔问题的时间复杂度

4.1题目描述

在经典汉诺塔问题中,有3根柱子及N个不同大小的穿孔圆盘,盘子可以滑入任意—根柱子。—开始,所有盘子自上而下按升序依次套在第一根柱子上(即每一个盘子只能放在更大的盘子上面)。移动圆盘时受到以下限制:

  • (1)每次只能移动一个盘子;
  • (2)盘子只能从柱子顶端滑出移到下—根柱子;
  • (3)盘子只能叠在比它大的盘子上。

请编写程序,用栈将所有盘子从第—根柱子移到最后—根柱子。
你需要原地修改栈。

4.2代码实现

class Solution {
    public void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {
        move(A.size(), A, B, C);

    }
    void move(int n, List<Integer> A, List<Integer> B, List<Integer> C){
        if (n == 1){
            C.add(A.get(A.size() - 1));
            A.remove(A.size() - 1);
        }
        else {
        move(n - 1, A, C, B);
        C.add(A.get(A.size() - 1));
        A.remove( A.size() - 1);
        move(n - 1, B, A, C);
        }
    }
}

4.3时间复杂度分析

  • 第一步:A柱的 n − 1 n-1 n1移到B柱所需时间T(n - 1)
  • 第二步:A柱的最后一个盘子移动到C所需时间1
  • 第三步:B柱的 n − 1 n - 1 n1个盘子移动到C柱所需时间

总共所需时间:T(n) = 2T(n - 1) + 1
得到得到 T ( n ) = 2 n − 1 T(n) = 2^n - 1 T(n)=2n1
所以汉若塔问题的时间复杂度为 O ( 2 n ) O(2^n) O(2n)

5递归法时间复杂度分析

递归方程如下
{ T ( 1 ) = 1 , T ( n ) = 2 T ( n 2 ) + n . n > 1 \left\{ \begin{array}{lr} T(1) = 1, & \\ T(n) = 2T(\frac{n}{2}) + n. \quad n > 1\\ \end{array} \right. {T(1)=1,T(n)=2T(2n)+n.n>1
分析如下
第一次
T ( n ) = 2 T ( n 2 ) + n = 2 [ 2 T ( n 2 2 ) + n 2 ] + n = 2 2 T ( n 2 2 ) + 2 n T(n) = 2T(\frac{n}{2}) + n = 2[2T(\frac{n}{2^2}) + \frac{n}{2}] + n = 2^2T(\frac{n}{2^2}) + 2n T(n)=2T(2n)+n=2[2T(22n)+2n]+n=22T(22n)+2n
= 2 2 [ 2 T ( n 2 3 ) + n 2 2 ] + 2 n \qquad = 2^2[2T(\frac{n}{2^3}) +\frac{n}{2^2}]+2n =22[2T(23n)+22n]+2n
数学归纳法可得
T ( n ) = 2 k T ( n 2 k ) + k n T(n) = 2^kT(\frac{n}{2^k})+kn T(n)=2kT(2kn)+kn
终止条件
n 2 k = 1 ⟹ k = log ⁡ 2 n \frac{n}{2^k} = 1 \Longrightarrow k=\log_2n 2kn=1k=log2n
代入可得
T ( n ) = n T ( 1 ) + n log ⁡ 2 n = n + n log ⁡ 2 n T(n) = nT(1)+n\log_2n= n+n\log_2n T(n)=nT(1)+nlog2n=n+nlog2n
所以时间复杂度为 O ( n + n log ⁡ 2 n ) = O ( n log ⁡ 2 n ) O(n+n\log_2n)=O(n\log_2n) O(n+nlog2n)=O(nlog2n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值