一、复杂度

什么是复杂度

说到复杂度可能很多小伙伴会比较反感,尤其是说到 数据结构复杂度算法 身边有些小伙伴就会无比的兴奋(咳咳,不对,是无比的反感!😜说实在的,我也很反感。但路总是要一步一步的走,必经之路再难走,也要尝试着去走下去不是么?😢😢😢),接下来我可能要以另外一种形式去学习 数据结构复杂度算法。 Let’s go!!!

时间复杂度与什么有关

在说复杂度之前,我们可以先说说,复杂度和计算机有什么关系,计算机去处理程序的本质,其实就是对数据的处理,那么对于数据的处理时计算式使用到了什么呢?内寸,CPU,(注意: 这里的内存和CPU是一个笼统的概念,不要跟我抬杠,抬杠的话可以去工地,那里缺少抬杠的,正在招人中。)基本上这两种就可以搞定了数据的处理,那么数据的处理需要是需要我们去等待还是不需要等待呢?这个就要去取决于处理数据的量的大小了。

如果我们要让我们的计算机去处理一批数据,那么我们就需要把这一批数据输入到计算机中,然后计算机会使用内存把数据存储起来,然后利用CPU的计算能力去对数据进行处理,然后再把存储好的数据存到内存中,最终会展示给我们看到最终的处理结果。说到这儿我们只说了计算机做了什么,但是我们输入完数据以后做了什么呢?(小朋友,你是否有很多问号???。。。。。)我们爱干啥爱干,你干啥我也不知道呀,但是我知道在没事的情况下肯定是等待着计算机去把数据计算完输出结果。这里我们等待的就是 时间 ,也就是说计算机处理数据是需要一个时间的,是需要一个过程的。

举一个生活中的例子:每次晚上加班回家得我时候我都会打车回家,打车回家的途中要走一段高速,在没有到高速收费站的时候我可以有两个选择,一个是走高速直接去收费站(收费10元),一个是走辅路然后到收费站上高速(收费5元),假设,在到收费站之前没有高速,只有一个辅路,但是辅路上有红绿灯,我要从这里走就要等待红绿灯(因为假设只有这一个辅路,没有其他高速),那我只能花时间去排队,去等待红绿灯方形车辆。但是如果政府拿出来辅路旁边的一块地进行修了高速,这个时候我就有了两个选择,1.多花钱走高速,2.少花钱继续等待红绿灯。我们来分析下,修路消耗了什么?消耗了财产(每个人都会花钱,可以中和消耗的财产)和地块(这个占地是没办法中和的,这是占用的土地资源),这里我们就可以来分析一下,想要回到家里都消耗了什么,如果走辅路上高速,但是要花费时间等待比较长。如果直接走高速,就需要消耗更多的土地资源。所以如果直接走高速,就等价于消耗了空间资源,来换取了时间资源。

回到正题:(继续讨论计算机)复杂度是衡量代码运行效率的重要的度量因素。 这句话理解起来可能比较费劲,复杂度到底是个什么呢?刚开始我们说到了,代码的执行牵扯到了内存和等待时间,那么内存和时间的消耗就统称为复杂度了,即复杂度又分为了两个纬度:空间复杂度时间复杂度

说完了复杂度,接着说一下复杂度的计算,复杂度是一个关于输入数据量 n 的函数。 假设你的代码复杂度是 f(n),那么就用个大写字母 O 和括号,把 f(n) 括起来就可以了,即 O(f(n))。例如,O(n) 表示的是,复杂度与计算实例的个数 n 线性相关;O(logn) 表示的是,复杂度与计算实例的个数 n 对数相关(这里先记住就好了,不要问我为什么,记住就完了)。

复杂度这个函数的计算方法又遵循以下几个原则:

  • 复杂度与具体的常系数无关, 例如 O(n) 和 O(2n) 表示的是同样的复杂度。我们详细分析下,O(2n) 等于 O(n+n),也等于 O(n) + O(n)。也就是说,一段 O(n) 复杂度的代码只是先后执行两遍 O(n),其复杂度是一致的。
  • 多项式级的复杂度相加的时候,选择高者作为结果,例如 O(n²)+O(n) 和 O(n²) 表示的是同样的复杂度。具体分析一下就是,O(n²)+O(n) = O(n²+n)。随着 n 越来越大,二阶多项式的变化率是要比一阶多项式更大的。因此,只需要通过更大变化率的二阶多项式来表征复杂度就可以了。
  • 注意: O(1) 也是表示一个特殊复杂度,含义为某个任务通过有限可数的资源即可完成。此处有限可数的具体意义是,与输入数据量 n 无关。

下面来看两个小🌰(例子):

话不多说,上代码

public function test1()
{
    $data = [1, 2, 3, 4, 5];
    $tmpData = [];
    for ($i = 0; $i < count($data); $i++) {
        $tmpData[$i] = $data[$i];
    }
    for ($i = 0; $i < count($data); $i++) {
        $tmpData[count($data) - $i -1] = $data[$i];
    }
    return $tmpData;
}

不难发现,这是一个数组翻转代码,输出的是 [5,4,3,2,1]。很明显这段代码执行次数是根据 $data 输入的长度来来决定的,即O(n),代码执行了两个for循环,则时间复杂度就是 O(n)+O(n),也就是 O(n)。代码中定义了一个数组$tmpData,长度也是根据$data来决定的,故空间复杂度也是O(n)。

下面我们来改进一下这个代码:

public function test2()
{
    $data = [1, 2, 3, 4, 5];
    $tmpData = 0;
    for ($i = 0; $i < count($data) / 2; $i++) {
        $tmpData = $data[$i];
        $data[$i] = $data[count($data) - $i -1];
        $data[count($data) - $i -1] = $tmpData;
    }
    return $data;
}

上面的代码中不难发现,只有一个for循环,时间复杂度继续保持O(n),但是定义的变量发生了变化,这次的$tmpData不再是一个数组,二十一个整型的变量,这里的变化跟$data的变化无关,无论$data怎么变化,$tmpData始终是一个整型变量,即空间复杂度为O(1),当数据量达到一定量级的时候就会节省不少的内存。

可见,对于同一个问题,采用不同的编码方法,对时间和空间的消耗是有可能不一样的。所以,我们在写代码的时候,一方面要完成任务目标;另一方面,也需要考虑时间复杂度和空间复杂度,以求用尽可能少的时间损耗和尽可能少的空间损耗去完成任务。

为什么要降低时间复杂度

我们了解了复杂度是什么,好多人都复杂度越低越好,就已有很多人去针对复杂度做大量的工作,那么江都复杂度真的有这个好吗?接下来我们就针对为什么要降低复杂度来掰扯掰扯了。

我们来看个例子你就明白为什么要降低复杂度了。

假设某个计算任务需要处理 10万 条数据。我们针对写出来的代码的复杂度做一个计算。

  • 如果是 O(n),那么计算的次数就是 10万 次左右(输入量 n 是10万次)。
  • 如果是 O(n²) 的时间复杂度,那么计算的次数就大概是 100 亿次左右(数据是不是很可怕?)。
  • 如果能够保证复杂度能在 O(log n) 的复杂度下完成任务,那么计算的次数就是 17 次左右(log 100000 = 16.61,计算机通常是二分法,这里的对数可以以 2 为底去估计,惊不惊喜意不意外刺不刺激)。

其实通常在小数据集上,时间复杂度的降低在绝对处理时间上没有太多体现。但在当今的大数据环境下,时间复杂度的优化将会带来巨大的系统收益。而这是必须具备的工程开发基本意识。

总结

今天的内容即将结束了,如果你还没过瘾,那就期待下一节吧,前提是你得把本节的内容掌握牢固了才可以哦。

总结一下知识点:

复杂度通常包括时间复杂度和空间复杂度。在具体计算复杂度时需要注意以下几点。

  • 复杂度与具体的常系数无关,O(n) 和 O(2n) 表示的是同样的复杂度。
  • 复杂度相加的时候,选择高者作为结果,也就是说 O(n²)+O(n) 和 O(n²) 表示的是同样的复杂度。
  • O(1) 也是表示一个特殊复杂度,即任务与算例个数 n 无关。
  • 时间复杂度与代码的结构设计息息相关。
  • 空间复杂度与代码数据结构息息相关。

最后:哪里理解的不对,哪里写的不好,需要改正或者优化的地方,欢迎指正,有兴趣的小伙伴也可以一起交流,相互学习。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值