复杂度快速入门(基础篇)

复杂度快速入门(基础篇)

前言

我们都知道数据结构算法的作用是让我们的代码在尽可能简洁的同时高效的完成业务的需求,那么我们该如何快速的判断我们代码的效率呢,最常用的办法就是将代码完成后进行一些压力测试从而来判断我们代码的效率,在一些书本中,将这种方法称为事后统计法,但是这种办法存在许多的局限性,所以为了能够更加快速的有效的判断代码的复杂度,大O复杂度表示法就诞生了。

事后统计法

在前言中我提到了事后统计法,那么这个方法有些什么弊端呢,总结如下:

  • 测试的结果非常的依赖测试环境
  • 测试的结果受到数据规模的影响
  • 测试成本非常大

大O表示法

算法的执行的效率,说白了就是算法的执行时间,通过使用事后统计法,我们可以得到某个算法的执行时间,但是这并不准确,而且还需要代码的测试,需要一些时间的成本,那么我们如何在不执行代码的的同时快速的判断代码的运行时间呢,请看下面这段代码:

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

这是一段非常简单的代码,那么我们如何计算这个代码的运行时间呢?
首先我们先设定执行一行代码的时间为time,第2,3行代码的执行时间就是2time,4.5行代码是一个循环,一共循环n次,那么这两行代码的执行时间就是2ntime,那么这段代码的执行时间T=2time+2ntime
这样我们就简单的算出来了这段代码的执行时间。通过观察这一个时间表达式,我们发现虽然我们并不知道time的具体时间,但是我们能看出T的大小是和n成正比的,所以会出现下面这个公式

T(n) = O(F(n))
先来简单的解释一下这个公式:T(n)代表的就是代码运行的时间,n是代码的数据量,f(n) 表示每行代码执行的次数总和。因为这是一个公式,所以用 f(n) 来表示。公式中的 O,表示代码的执行时间 T(n) 与 f(n) 表达式成正比
所以例子中T(n) = O(2n+2),这就是大O表达式,大 O 时间复杂度实际上并不具体表示代码真正的执行时间,而是表示代码执行时间随数据规模增长的变化趋势,所以,也叫作渐进时间复杂度(asymptotic time complexity),简称时间复杂度。当 n 很大时,你可以把它想象成 10000、100000。而公式中的低阶、常量、系数三部分并不左右增长趋势,所以都可以忽略。我们只需要记录一个最大量级就可以了,如果用大 O 表示法表示刚讲的那两段代码的时间复杂度,就可以记为:T(n) = O(n)。

时间复杂度的分析

前面说了大O表达式,那么我们应该如何判断时间复杂度呢,这里有三个方法:

  1. 只关注循环执行次数最多的一段代码
    大O表达发表示的只是一种趋势,所以我们可以忽略公式中的低阶,常量,系数三部分,因为其并不对趋势产生影响,即使常量的数字非常大10000,20000我也可以把它忽略,他可能对速度会产生影响,但是并不会对趋势产生影响,所以我们在分析时间复杂度的时侯,可以把重点方法大的数量级上,就比如说那种循环数最多的代码,这个循环的次数n就是我们的数量级,如下面代码
int cal(int n) {
   int sum = 0;
   int i = 1;
   for (; i <= n; ++i) {
     sum = sum + i;
   }
   return sum;
 }

这段代码中第2,3行代码都是常量级的时间,所以我们可以直接把它忽略掉,看下面的循环,一共循环了n次,所 以这 段代码的时间复杂度就是O(n)。

  1. 加法法则:总复杂度等于量级最大的那段代码的复杂度
int cal(int n) {
   int sum_1 = 0;
   int p = 1;
   for (; p < 100; ++p) {
     sum_1 = sum_1 + p;
   }

   int sum_2 = 0;
   int q = 1;
   for (; q < n; ++q) {
     sum_2 = sum_2 + q;
   }
 
   int sum_3 = 0;
   int i = 1;
   int j = 1;
   for (; i <= n; ++i) {
     j = 1; 
     for (; j <= n; ++j) {
       sum_3 = sum_3 +  i * j;
     }
   }
 
   return sum_1 + sum_2 + sum_3;
 }

这段代码可以简单的分为三段代码,我们可以分别求出三段代码的时间复杂度,首先第一段代码中都是常量级时间,例如p<100,刚才我们说过只要是常量及时间我们都可以忽略,所以第一段代码我们可以忽略,第二段代码他的循环次数是n次,所以他的时间复杂度是O(n),第三段代码,他的循环次数是nn次,所以他的时间复杂度是O(nn).所谓的加发法则就是在这几个时间复杂度中取最大的那个,所以这整段代码的时间复杂度就是O(n*n).

  1. 乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积
    所谓的乘法法则,就是在程序中如果有嵌套的循环,那么外层的时间复杂度乘内层的时间复杂度,案例如下:

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;
 }

根据刚才所讲,我们可以算出这段代码的时间复杂度为O(n*n)。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值