算法之复杂度分析(第一天)

前言:学习算法第一步,复杂度分析。复杂度分析是算法基础中的基础,万丈高楼平地起,要想大楼盖的高,地基必须得打牢。

为什么需要复杂度分析?

我们可以把代码跑一遍,通过统计、监控等,得到算法的执行时间和占用内存。这种统计方法叫做事后统计法。有非常大的局限性。

例如:在不同机器上同样的代码执行的时间不一样。测试数据规模太小,测试结果可能无法真实地反映算法的性能。即:

  1. 测试结果非常依赖测试环境
  2. 测试结果受数据规模的影响很大

所以需要一个不用具体的测试数据来测试,就可以粗略的估计算法的执行效率的方法。

大O 复杂度表示法
 int cal(int n) {
   int sum = 0;
   int i = 1;
   for (; i <= n; ++i) {
     sum = sum + i;
   }
   return sum;
 }

假设每行代码执行的时间是一样的,为unit_time。那么这段代码的总执行时间是多少?

第2、3 行分别需要1 unit_time的执行时间,第4、5 行都运行了n 遍,所以需要2n*unit_time,所以这段代码总的执行时间是(2n+2) * unit_time。可以看出来,所有代码的执行时间与每行代码的执行次数成正比。

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

  • T(n) 表示代码执行的时间。

  • n 表示数据规模的大小。

  • f(n) 表示每行代码执行的次数总和。因为这是一个公司,所以用f(n) 来表示。

  • O 表示代码的执行时间T(n) 与f(n) 成正比。

注:大O 时间复杂度实际上并不具体表示代码真正的执行时间,而是表示代码执行时间随数据规模增长的趋势,所以,也叫做渐进时间复杂度,简称时间复杂度。

公式中的低阶、常量、系数三部分并不左右增长趋势,所以都可以忽略,只需要记录一个最大量级就可以了。

所以我们上面那个例子的T(n) = O(2n+2),即T(n) = O(n)。

怎么做时间复杂度分析
  1. 只关注循环执行次数最多的一段代码
  2. 加法法则:总复杂度等于量级最大的那段代码的复杂度
  3. 乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂的的乘积
常见的时间复杂度实例分析
  • 常量阶O(1)
  • 对数阶O(logn)
  • 线性阶O(n)
  • 线性对数阶O(nlogn)
  • 平方阶O(n2),立方阶O(n3),… K次方阶O(n^k)
  • 指数阶O(2^n)
  • 阶乘阶O(n!)

其中非多项式量级只有两个指数阶O(2^n)、阶乘阶O(n!)。当数据规模 n 越来越大时,非多项式量级算法的执行时间会急剧增加,求解问题的执行时间会无限增长。所以,非多项式时间复杂度的算法其实是非常低效的算法。

注:什么是单项式,什么是多项式。
由数和字母的积组成的代数式叫做单项式,单独的一个数或一个字母也叫做单项式。由若干个单项式相加组成的代数式叫做多项式。
我个人理解,有不对的地方,可以在下方评论区留言。
就只看上方加粗的时间复杂度,猛地一看他们是单项式的。但是我们在做时间复杂度分析的时候,把低阶、常量、系数这三部分都给忽略掉了,所以实际上上方加粗的时间复杂度,或多或少都带有低阶、常量或者系数,所以他们都是多项式量级的复杂。那两个非多项式量级的怎么理解,因为多项式是由单项式相加组成的代数式叫做多项式,而指数和阶乘并不满足这个条件,所以他们既不是单项式,也不是多项式。

来看几种常见的多项式时间复杂度。

  1. O(1)

    首先你必须明确一个概念,O(1) 只是常量级时间复杂度的一种表示方法,并不是指只执行了一行代码。比如这段代码,即便有 3 行,它的时间复杂度也是 O(1),而不是 O(3)。

     int i = 8;
     int j = 6;
     int sum = i + j;
    

    只要代码的执行时间不随 n 的增大而增长,这样代码的时间复杂度我们都记作 O(1)。或者说,一般情况下,只要算法中不存在循环语句、递归语句,即使有成千上万行的代码,其时间复杂度也是Ο(1)。

  2. O(logn)、O(nlogn)

  3. O(m+n)、O(m*n)

空间复杂度分析

定义:理解了时间复杂度,空间复杂度就非常简单了。类比时间复杂度,空间复杂度的全称是渐进空间复杂度,表示算法的存储空间与数据规模之间的增长关系。

常见的空间复杂度:O(1)、O(n)、O(n2 ),像 O(logn)、O(nlogn) 这样的对数阶复杂度平时都用不到。而且,空间复杂度分析比时间复杂度分析要简单很多。所以,对于空间复杂度,掌握这些内容已经足够了。

不懂就查两个点
  1. UAT:测试环境,UAT测试:俗称验收测试
  2. 学习了对数:log

最后留下个思考题,请计算下方代码的时间复杂度,欢迎下方留言

int cal(int n) {
   int ret = 0; 
   int i = 1;
   for (; i < n; ++i) {
     ret = ret + f(i);
   } 
 } 
 
 int f(int n) {
  int sum = 0;
  int i = 1;
  for (; i < n; ++i) {
    sum = sum + i;
  } 
  return sum;
 }

注:学习极客时间王争老师的《数据结构与算法之美》,自己当做笔记用,顺带做一个小分享,如有侵权,联系我删除。另外强烈推荐这门课程,真的是非常好。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值