网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
四、算法效率的度量方法 |
📘 四、算法效率的度量方法
通过上文的介绍,大家已经对算法有了初步的了解,但是,实际情况如何衡量一个算法的好坏呢,相信现在大家心里都还是存在着这个疑惑的,下面就来看看有哪些度量方法吧!
🔶 4.1、事后统计方法
定义:不同的算法设计出不同的测试程序进行测试,然后统计计算机计时器在不同测试程序运行的时间进行比较,从而确定算法效率的高低。
1.1、事后统计方法存在的问题
1、某个问题或某类问题可以存在多种算法解决,如果每种算法都编写测试程序需要花费大量的时间和精力。
2、运行效率依赖于计算硬件和软件等环境因素,不同的硬件和软件有可能会掩盖了算法存在的问题,导致测试出来的结果不准确。
3、测试数据设计准备困难,程序的运行效率往往和测试数据的规模有关,如果测试数据太小,以现在CPU的运算速度,根本看不出差异,选择多大的测试数据规模,测试多少次才能够得到比较准确的结果等,这些问题都是很难进行判断的。
1.2、事后统计方法结论
基于事后统计方式存在的缺陷,这种算法效率的度量方式是不推荐使用的。
🔷 4.2、事前统计方法
定义:在进行算法程序编写前,依据统计方法对算法进行估算,得到算法程序运行的预估值。
计算机的前辈们经过分析发现,通过高级程序语言编写的程序在计算机运行时消耗的时间取决于下面的几个因素:
通过上面的图片可以发现,因素2取决于软件的支持,因素4取决于运行程序的硬件,抛开软件、硬件有关的因素,一个程序的运行效率,依赖于算法的好坏和问题的输入规模(即输入量的多少)。
下面我们通过求和的两种算法来实际分析与程序的运行时间相关的具体因素:
算法一:累加求和
int i,sum=0,n=100; // 执行1次
for(i=1;i<n;i++){
sum = sum + i; // 执行n次
}
System.out.println("sum="+sum); // 执行1次
算法二:高斯算法
int i,sum=0,n=100; // 执行1次
sum = (1 + n) \* n / 2; // 执行1次
System.out.println("sum="+sum); // 执行1次
通过上面求和的两个算法我们会发现,如果将头尾变量定义和循环判断等开销忽略,实际上两种算法就是运行n次和1次的区别,随着n的数值越大,它们之间运行的次数差距也越大,程序花费的时间差距也越大,因此,我们可以得出一下的一个结论:
测定运行时间最可靠的方法,就是计算对运行时间有消耗的基本操作的执行次数,运行时间和这个计数成正比。
结论:
对于输入规模为n,第一种求和算法中,求和操作 sum = sum+ i
代码需要被运行n次,我们可以说这个问题的输入规模n使得程序的操作数量是:f(n) = n
对于输入规模为n,第二种算法中求和代码:sum = (1 + n) * n / 2
始终只需要执行一次,那么我们可以说这个问题的输入规模为n使得程序的执行数量是:f(n) = 1
因此,在进行分析一个算法的运行时间时,我们需要将输入规模和程序的基本操作数量进行关联起来即将基本操作数量表示成输入规模的函数
,如下图所示:
五、时间复杂度和空间复杂度 |
📙 五、时间复杂度和空间复杂度
🟧 5.1、时间复杂度
在数据结构中,使用时间复杂度来衡量程序运行时间的多少
。每条语句执行的次数称为该语句的频度,整段代码的总执行次数则称为整段代码的频度。
定义:在算法估算时,语句的执行次数T(n)是关于问题规模n的函数,从而分析T(n)随着n的变化的关系。算法的时间复杂度也称为算法的时间量度,记作T(n) = O(f(n)),它表示随着问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐进时间复杂度,简称为时间复杂度
,其中f(n)是问题规模n的某个函数。
刚开始看上面的定义多少有些迷惑,但是多读几次结合上文的知识串起来后,你会其实并没有这么复杂,时间复杂度其实可以理解为随着问题规模n的增大,程序语句执行频度的增长率。
在定义中我们使用到O()的方式来体现算法时间复杂度的记法,这种方式又称为大O记法。一般情况下,随着问题规模n的增大,T(n)即语句执行次数增长最慢的算法为最优算法,讲简单点,就是无论你输入规模如何变化,只要执行的语句次数增长最小,那这种算法就是最优的。
了解算法时间复杂度的定义,那么如何分析一个算法的时间复杂度呢(即如何推到大O阶呢)?没错,经过前辈们的经验,这个也是有相应的推导公式的,在推导的时候我们应该采用无限大的思想来简化大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、常见的时间复杂度例子
一、常数阶
int i,sum=0,n=100; // 执行1次
sum = (1 + n) \* n / 2; // 执行1次
System.out.println("sum="+sum); // 执行1次
这个算法就是上面我们举例到的高斯算法,程序的执行次数函数为f(n) = 3,根据大O阶方法的推导方式,则得到的时间复杂度为:O(1),而不是O(3)
,注意:因为执行函数并不会随着n的变化而变化,它是恒定的,像复杂度为O(1),又被称作常数阶。
二、线性阶
int i,sum=0,n=100; // 执行1次
for(i=1;i<n;i++){
sum = sum + i; // 执行n次
}
System.out.println("sum="+sum); // 执行1次
根据上面的代码,我们可以发现执行次数函数为f(n) = n,根据大O阶方法的推导方式得到它的时间复杂度表示为:O(n)。
像这种线性阶,我们主要分析的是循环结构中的一个运行情况,从而得到它的时间复杂度。
三、对数阶
int condition = 1;
while(condition < n){
condition = condition \* 2;
}
根据上面的代码,我们会发现循环语句的条件会在每次condition乘以2后更加接近跳出条件,既满足多少个与2的乘积后将会退出循环,因此我们可以得到执行次数的函数为:f(n) => 2x = n ===> x = log2n,根据大O阶方法的推导方式得到它的时间复杂度表示O(logn)。
四、平方阶
for(int i=o;i<n;i++){
for(int j=o;j<n;i++){
...
}
}
根据代码分析,我们可以得到执行次数的函数为: 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中,该书籍仅供会员在线阅读,无法下载,所以博主正在找电子版同步到【技术圈子】中,有需要的小伙伴可以扫描下面二维码进入【技术圈子】。
七、写在最后 |
🚀 七、写在最后
也许大家看完文章后会还是存在些许疑惑,这个是非常正常的,解决办法就是将不理解的概念整理出来,多看几遍,你会有不同的领悟,算法的学习对每一位有自我要求的开发者来说应该都是必须的,如果读完后还是存在疑惑,欢迎私信博主或者进入【技术圈子】提问。
推荐一下:
【技术圈子】目的是为了搭建共享资源平台,因为很多人寻找资源路径有限,但是人多力量大,将它们整理起来寻找资源的方式就多了,也更容易了。
博主保证:圈内所有资源都免费
,资源包括但不限于免费面试资源、简历模板、年终汇报PPT、CSDN VIP下载资源等等,无论你是大学生、还是已经工作的开发者,都期待您的加入!感兴趣者可以扫描下面二维码或者查看左边导航栏进入技术圈子。
如果文章对你有帮助,请帮忙点击"一键三连
" 和 “专栏订阅
”,感谢您的支持!期待下一篇文章继续看到你的身影!
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
继续看到你的身影!
[外链图片转存中…(img-iM345qQ5-1714914230310)]
[外链图片转存中…(img-xaKYaAJF-1714914230311)]
[外链图片转存中…(img-0RGeAmk1-1714914230311)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新