数据结构与算法 04 时间复杂度分析 → 大O记法

2.3 大O记法(大O表示法)


2.3.1 初识大O表示法

定义:

在进行算法分析时,语句总的执行次数T(n)是关于问题的输入规模n 的函数,进而分析 T(n) 随着 n 的变化情况并确定T(n)的量级。算法的时间复杂度,就是算法的时间度量,记作:T(n) = O(f(n))。它 表示随着问题规模n 的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐近时间复杂度,简称 “时间复杂度”,其中f(n)是问题规模n的某个函数。

在这里,我们需要明确一个事情:执行次数 = 执行时间

用大写O() 来体现算法时间复杂度的记法,这个大O表示法是怎么来的呢?是由 渐近增长函数 进行的每次推导总结而来的。所谓大O表示法,就是 只保留核心部分,忽略不重要部分!

下面我们使用大O表示法来表示一些求和算法的时间复杂度:

  • 算法①
packageO表示法;

public class 算法1 {
    public static void main(String[] args) {
        int sum = 0;//1
        int n = 100;//1
        sum = (n+1)*n/2;//1
        System.out.println(sum);//1
    }
}

你会发现 一共是 4次,那么就是 常量时间复杂度。


大O表示法优化为:O(n) = 1

  • 算法②
packageO表示法;

public class 算法2 {
    public static void main(String[] args) {
        int sum = 0;//1
        int n = 100;//1
        for(int i = 1;i<=n;++i)
        {
            sum+=i;//n
        }
        System.out.println(sum);//1
    }
}

总共次数: 3 + n 次


大O表示法优化为:O(n) = n

  • 算法③
packageO表示法;

public class 算法3 {
    public static void main(String[] args) {
        int sum = 0;
        int n = 100;
        for(int i = 1;i<n;++i)
        {
            for(int j = 1;j<=n;++j)
            {
                sum+=i;//1 + 2 + 3 ... + n = (1+n)*n / 2
            //1个1 + 2个2 + 3个3 + 4个4 +... +n个n
            }
        }
    }
}

总共次数:n2/2 + n/2 + 3


大O表示法优化为:O(n) = n2


2.3.2 常见的大O阶

  1. 线性阶

一般含有非嵌套循环设计线性阶,线性阶就是随着输入规模的扩大,对应计算次数呈现直线增长,例如:

packageO表示法;

public class 算法2 {
    public static void main(String[] args) {
        int sum = 0;//1
        int n = 100;//1
        for(int i = 1;i<=n;++i)
        {
            sum+=i;//n
        }
        System.out.println(sum);//1
    }
}

上面这段代码,它的时间复杂度是 n

  1. 平方阶

一般嵌套循环属于这种时间复杂度

packageO表示法;

public class 算法3 {
    public static void main(String[] args) {
        int sum = 0;
        int n = 100;
        for(int i = 1;i<n;++i)
        {
            for(int j = 1;j<=n;++j)
            {
                sum+=i;//1 + 2 + 3 ... + n = (1+n)*n / 2
            //1个1 + 2个2 + 3个3 + 4个4 +... +n个n
            }
        }
    }
}

上面 这段代码,n = 100,也就是说,外层循环每执行一次,内层循环就执行100次,那总共程序想要从这两个循环中出来,100*100次,也就是 n 的 平方次,所以这段代码的时间复杂度是O(n2)。

  1. 对数阶

对数,属于高中数学的内容,我们分析程序以程序为主,数学为辅,所以不用过分担心。

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

由于 每次 i*2 之后,就距离 n 更近一步,假设有x个2相乘后大于n,则会退出循环。由于是2x=n,得到x = log2n,所以这个循环的时间 复杂度为O(log n)。


对于对数阶,由于随着输入规模n 的增大,不管底数为多少,他们的增长趋势是一样的,所以我们会忽略底数。

为什么可以省略 底数 ?

答:如图所示
在这里插入图片描述
在这里插入图片描述
无论你的 底数是什么,随着输入规模 n 的增大,它们 都是 非常 靠近的。(函数增长的 趋势 是差不多的。)

  1. 常数阶

一般不涉及循环操作的都是常数阶,因为 它不会随着 n 的增长而增长操作的次数。例如:

public static void main(String[] args)
{
	int n = 10;
	int i = n + 2;
	System.out.println(i);
}

上述代码,不管输入规模 n 是多少都会执行 2 次,根据 大 O 推导法则,常数用 1 来替代。所以 上述代码的时间复杂度O(n) = 1

描述增长的数量级说明举例
常数级别1普通语句将两个数相加
对数级别logN二分策略二分查找
线性级别N循环找出最大元素
线型对数级别NlogN分治思想归并排序
平方级别N2双层循环检查所有元素对
立方级别N3三层循环检查所有三元组
指数级别2N穷举查找检查所有子集
  • 它们的时间复杂地 从低 到 高 依次为:

O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3)

根据前面的折线图分析,我们会发现,从平方阶开始,随着输入规模的增大,时间成本会急剧增大,所以,我们的算法,尽可能的追求的是O(1),O(logn),O(nlogn) 这几种时间复杂度,而如果发现算法的时间复杂度为平方阶,立方阶或者更复杂的,那么我们可以分为这种算法是不可取的,需要优化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值