- 💂 个人主页: IT学习日记
- 🤟 版权: 本文由【IT学习日记】原创、在CSDN首发、需要转载请联系博主
- 💬 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦
- 💅 想寻找共同成长的小伙伴,请点击【技术圈子】
文章大纲
- 📔 一、前言
- 📕 二、专栏推荐
- 📗 三、了解算法
- 🔴 3.1、学习算法的必要性
- 🔵 3.2、何为算法
- 🟣 3.4、算法的特征
- 🟡 3.4、算法和程序的区别
- 📘 四、算法效率的度量方法
- 🔶 4.1、事后统计方法
- 🔷 4.2、事前统计方法
- 📙 五、时间复杂度和空间复杂度
- 🟧 5.1、时间复杂度
- 🟨 5.2、常见的时间复杂度例子
- 🟦 5.3、空间复杂度
- 📜 六、参考资料
- 🚀 七、写在最后
📔 一、前言
-
大家好,我是小诚,国庆放假后跟一些小伙伴聊天时发现,大家潜意识里都知道想要进入大厂算法是必须过关的,所以很多人在学校就开始去刷题了,题目虽然刷了许多,但是对于学习算法的初衷和衡量一个算法的指标却是模糊的,所以,博主想写一篇关于学习算法的初衷和算法的指标,帮助准备学习算法或者初学算法的小伙伴将基础巩固。
-
本篇文章重点介绍:
-
📕 二、专栏推荐
良心推荐: 下面的相关技术专栏还在免费分享哦,大家可以帮忙点点订阅哦!
面试干货专栏
JAVA进阶知识专栏
从0到1-全面深刻理解MySQL系列专栏
📗 三、了解算法
🔴 3.1、学习算法的必要性
说明: 必要性就从实际用途方面介绍(至于那些:算法是进入大厂必备的技能这种原因就跳过了)。
看过我之前博客的小伙伴应该知道,我在很多博文中都有提到一句话,“”,那算法出现的必要性是因为什么呢,在开发者的世界中有一种说法:,下面来举个例子,让大家有更直观的感受。
大家都了解计算机中主要的计算组件就是CPU,假设某个问题可以通过两种算法(简称为算法1、算法2)解决,它们的时间复杂度分为O(n)和O(n²)(时间复杂度不懂的没关系,后面会介绍),假设现在某型号CPU的运算速度提升了100倍,我们分别配置稍微低一点的CPU去跑时间复杂度为O(n)的算法,和使用提升了100倍的CPU去跑时间时间复杂度为O(n²)的算法,我们会发现一个结果:,这是为什么?
原因就在于算法的复杂度, 搭配了提升100倍运算速率CPU的计算机就好好比生活中的小车,搭配了硬件配置较低的计算机就好比生活中的单车,如果单车的速度都比小车快(单单考虑速度方面),你还会考虑购买小车?毕竟小车的价格是单车的几倍?
通过上面简单的例子,我们会发现:
🔵 3.2、何为算法
上面一小节,我们通过具体的例子解释了学习算法的必要性,那么接下来我们就来认识下什么是算法。
并不是什么高深的词汇,,就好像我们想去旅游,可以乘坐高铁动车、也可以坐飞机。
就好比如果旅游的目的地比较近,可以乘坐高铁,这样可以节省花费,如果比较远,可以考虑乘坐飞机,可以直达,避免中间换乘等时间和金钱的花费。
那如何才能从多种算法中选择最好的最好的一种呢?想要实现这个目的,那我们需要先了解衡量一个算法好坏的指标是那些,然后再根据这些指标去进行选择。
🟣 3.4、算法的特征
上一节中提到:算法就是解决某个或者某类问题的办法,但是,这只是对算法的一个笼统的描述。
-
输入输出: 算法具有零个或者多个输入,至少一个或者多个输出。输入可以为零,但是必须存在输出,输出的形式可有为确定的返回值或者日志的的打印等,如果没有输出,那算法的意思在哪里呢?
-
有穷性: 指算法在执行有限的步骤后可以自动结束,不会出现无限的循环,且每个步骤在可以接受的时间完成。注意:这里的有穷性并不是单纯数学上的概念,它指的是一个可以接收的“边界”,如存在某种算法运行几年后可以得出结果,理论上也是符合有穷性。
-
确定性: 算法的每一个步骤都是具有确定的意义,不会出现歧义,相同的输入必须得到相同的输出。
-
可行性: 算法中每一步都可以转换成程序在计算机上运行,它意味着算法中的设计是可行的,但是并不意味着一定得到正确的结果。
上一节我们介绍了一个算法具有的哪些特征,,那么,衡量一个算法好坏有哪些标准呢?下面具体来看看吧!
正确性:要求算法应该有输入、输出和执行无歧义,能够正确反映问题的具体需求,通过算法能够得到解决问题的正确答案。
2.1.1、“正确性”算法的四个层次
(1)算法程序没有语法错误
(2)算法程序对合法的输入能够得到满足要求的输出
(3)算法程序对非法的输入能够得到满足规格的输出
(4)算法程序对故意刁难的测试数据能够得到满足要求的输出
对于算法的第4层次,基本不可能实现将所有的输入逐一验证,所以算法的正确性一般只要满足到第3层次即可。
,因为人们无法轻易读懂它,导致难以修改和调试,即使算法中存在问题也不能轻易发现,最后可能算法的设计者在时间久远后也很难理解它。
好的算法应该对不合理的输入做到适当的处理(提示等),而不是产生让使用者也无法理解的异常或者莫名其妙的结果。
健壮性定义: 算法的输入数据不合法时,算法能够做出相应的适当处理,而不是产生难以理解的异常或者莫名其妙的结果。
算法也是来源生活,生活中我们都希望花最少的钱办最多的事,算法也是一样,能够占用最小的空间,最快的得到结果那就是一个好的算法。
时间效率高: 指的是通过算法设计的程序执行的时间短,一个问题存在着多种算法可以解决,使用时间最短的那个时间效率最高。
存储量低: 存储量是指实现算法的存储在运行时占用最大的存储空间,程序运行占用的空间越小,证明运行程序需求的资源越小,算法就越好(存储空间一般是指:内存或者硬盘等设备的存储空间)。
🟡 3.4、算法和程序的区别
有些小伙伴可能会将算法和程序两者的概念混淆,其实它们两者是不同的概念。
就比如我需要输出1到100范围内的所有数值,我们会考虑到使用循环语句输出,使用循环语句输出这个思路就可以说是算法,然后实际上根据这个思路编写出来的代码就可以称为程序。
📘 四、算法效率的度量方法
通过上文的介绍,大家已经对算法有了初步的了解,但是,实际情况如何衡量一个算法的好坏呢,相信现在大家心里都还是存在着这个疑惑的,下面就来看看有哪些度量方法吧!
🔶 4.1、事后统计方法
定义:不同的算法设计出不同的测试程序进行测试,然后统计计算机计时器在不同测试程序运行的时间进行比较,从而确定算法效率的高低。
1、某个问题或某类问题可以存在多种算法解决,如果每种算法都编写测试程序需要花费大量的时间和精力。
2、运行效率依赖于计算硬件和软件等环境因素,不同的硬件和软件有可能会掩盖了算法存在的问题,导致测试出来的结果不准确。
3、测试数据设计准备困难,程序的运行效率往往和测试数据的规模有关,如果测试数据太小,以现在CPU的运算速度,根本看不出差异,选择多大的测试数据规模,测试多少次才能够得到比较准确的结果等,这些问题都是很难进行判断的。
基于事后统计方式存在的缺陷,这种算法效率的度量方式是不推荐使用的。
🔷 4.2、事前统计方法
计算机的前辈们经过分析发现,通过高级程序语言编写的程序在计算机运行时消耗的时间取决于下面的几个因素:
通过上面的图片可以发现,因素2取决于软件的支持,因素4取决于运行程序的硬件,
下面我们通过求和的两种算法来实际分析与程序的运行时间相关的具体因素:
通过上面求和的两个算法我们会发现,如果将头尾变量定义和循环判断等开销忽略,实际上两种算法就是运行n次和1次的区别,随着n的数值越大,它们之间运行的次数差距也越大,程序花费的时间差距也越大,因此,我们可以得出一下的一个结论:
对于输入规模为n,第一种求和算法中,求和操作 代码需要被运行n次,我们可以说这个问题的输入规模n使得程序的操作数量是:
对于输入规模为n,第二种算法中求和代码: 始终只需要执行一次,那么我们可以说这个问题的输入规模为n使得程序的执行数量是:
,如下图所示:
📙 五、时间复杂度和空间复杂度
🟧 5.1、时间复杂度
。每条语句执行的次数称为该语句的频度,整段代码的总执行次数则称为整段代码的频度。
定义:在算法估算时,语句的执行次数T(n)是关于问题规模n的函数,从而分析T(n)随着n的变化的关系。,其中f(n)是问题规模n的某个函数。
刚开始看上面的定义多少有些迷惑,但是多读几次结合上文的知识串起来后,你会其实并没有这么复杂,
在定义中我们使用到O()的方式来体现算法时间复杂度的记法,这种方式又称为大O记法。一般情况下,随着问题规模n的增大,T(n)即语句执行次数增长最慢的算法为最优算法,
了解算法时间复杂度的定义,那么如何分析一个算法的时间复杂度呢(即如何推到大O阶呢)?没错,经过前辈们的经验,这个也是有相应的推导公式的,,具体如下:
-
用常数1代替运行时间中的所有加法的常数,如:某个算法的执行函数为f(n) = 10,则替换成大O阶方法的话则为:O(1),无论这个常数为10,还是100,还是1000都使用1替换,因为执行函数和问题规模n的大小无关,编程算法基础它是执行时间恒定的,像时间复杂度为O(1)的又被称作常数阶。
-
如果表达式有多项含有无限大变量的式子,只保留一个拥有指数最高的变量的式子。例如 2n²+2n 简化为 2n²;
-
如果最高阶项存在且系数不为1,则去除掉与这个项相乘的系数,例如 2n² 系数为 2,直接简化为 n² ;
经过上面三个步骤推到出来的结果就是算法对应的大O阶。
一种是计算所有情况的平均值,这种时间复杂度的计算方法称为平均时间复杂度。另一种情况则为计算最坏情况下的时间复杂度,这种也称为最坏时间复杂度,
🟨 5.2、常见的时间复杂度例子
这个算法就是上面我们举例到的高斯算法,,注意:因为执行函数并不会随着n的变化而变化,它是恒定的,像复杂度为O(1),又被称作常数阶。
根据上面的代码, 像这种线性阶,我们主要分析的是循环结构中的一个运行情况,从而得到它的时间复杂度。
根据上面的代码,我们会发现循环语句的条件会在每次condition乘以2后更加接近跳出条件,既满足多少个与2的乘积后将会退出循环,因此我们可以得到执行次数的函数为:f(n) => 2x = n ===> x = log2n,
根据代码分析,我们可以得到执行次数的函数为: f(n) = n2,根据大O阶方法的推导方式得到它的时间复杂度表示O(n2)。
O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2n ) < O(n!) < O(nn)
🟦 5.3、空间复杂度
,跟时间复杂度类似,它也可以使用大O记法来表示。
算法的空间复杂度是通过计算算法所需的存储空间实现的,计算公式为:S(n) = O(f(n)),其中n为问题的规模,f(n)则为语句关于问题规模n所占存储空间的函数,随着n的变动,f(n)的增长率越小越好。
-
,这部分的存储空间主要取决于程序的代码量,因此,为了减少这部分占用的空间,在保证算法的合理性情况下,应该尽量减少代码量。
-
,这部分的占用的空间主要取决于不同算法的实现逻辑,但是大体上它们的大小都是相差不大的。
-
,因为不同的算法实现细节可能存在较大的差异,会申请的空间也会存在比较大的不同。
-
如果算法执行时所需要的空间和算法的输入值无关,对于输入数据量来说是一个常数的话,则称该算法为 空间复杂度为O(1)。
-
如果随着输入数据量 n 的增大,程序申请的临时空间成线性增长,则程序的空间复杂度用 O(n) 表示。
-
如果随着输入数据量 n 的增大,程序申请的临时空间成 n2 关系增长,则程序的空间复杂度用 O(n2) 表示。
-
如果随着输入数据量 n 的增大,程序申请的临时空间成 n3 关系增长,则程序的空间复杂度用 O(n3) 表示。
-
等等()
,然后再结合具体的算法(leetcode网站)进行实战,有了理论知识的支撑,相信你实战的脚本会更加快捷。
📜 六、参考资料
《大话数据结构》
说明: CSDN中,该书籍仅供会员在线阅读,无法下载,所以博主正在找电子版同步到【技术圈子】中,有需要的小伙伴可以扫描下面二维码进入【技术圈子】。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ji-chu/272.html