算法的复杂度分析

这篇具有很好参考价值的文章主要介绍了算法的复杂度分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

算法的复杂度分析,数据结构与算法,算法

[王有志](https://www.yuque.com/wangyouzhi-u3woi/dfhnl0/hqrch62un0cc9sp2?singleDoc# 《🔥快来关注我》),一个分享硬核Java技术的互金摸鱼侠
加入Java人的提桶跑路群:[共同富裕的Java人](https://www.yuque.com/wangyouzhi-u3woi/dfhnl0/nwry2mdlktok50bt?singleDoc# 《🔥共同富裕的Java人》)

今天我们只有一个内容:算法的复杂度分析。算法的复杂度分析可以说是算法中的灵魂,有了它我们才能去评价一个算法优劣。

算法的评价标准

我们可以套用“多快好省”这个标准去衡量算法:

  • ,适用场景多,适用于一个问题的算法没有太大的意义;
  • ,运行速度快,过慢的算法没有太大的意义;
  • ,代码质量好,优雅的实现和健壮的程序;
  • ,占用资源省,用得越省算法越好。

有了衡量算法的标准,我们还需要一套衡量算法的方法。

算法的复杂度分析

算法是解决一类问题思想,因此我们不必关注的标准;的标准虽然有一定的共识,如可读性,健壮性,但是无法量化。而是通过执行时间内存占用来体现的,可以进行量化分析。
通常我们将算法的执行时间和内存占用统称为算法执行效率,而对算法执行效率的分析称为算法复杂度分析。
算法的执行效率,会受到问题规模硬件环境的影响。在设计算法时,我们无法预测算法执行的硬件环境,因此我们需要一种能够忽略硬件环境,并能客观展示算法的执行效率随问题规模增长而改变的分析方法。

渐进复杂度分析

相信你一定听说过“大O记号”和“(渐进)时间复杂度”吧?
实际上这就是通过渐进分析得到的结果。我们先来看下邓俊峰老师的解释:

在评价算法运行效率时,我们往往可以忽略其处理小规模问题时的能力差异,转而关注其在处理更大规模问题时的表现。其中的原因不难理解,小规模问题所需的处理时间本来就相对更少,故此时不同算法的实际效率差异并不明显;而在处理更大规模的问题时,效率的些许差异都将对实际执行效果产生巨大的影响。这种着眼长远、更为注重时间复杂度的总体变化趋势和增长速度的策略与方法,即所谓的渐进分析(asymptotic analysis)。

这段话不难理解,简单来说就是,渐进分析关注的是算法执行效率随问题规模增长的变化趋势和增长速度。如果绘制成函数曲线,我们就是要看这条曲线“陡不陡”。
如果将执行效率拆分开来,算法的复杂度又可以分为渐进时间复杂度和渐进空间复杂度。
渐进时间复杂度分析中,可以粗略的认为每行代码的执行时间是一致的,从而对代码执行次数进行分析。如果借助了编程语言的工具库,还需要考虑这部分的时间成本。
渐进空间复杂度分析中,原始输入的数据不计入到空间占用中,只有在算法中创建的才会计入
随着硬件技术的发展,内存越来越廉价,在设计算法时,也可以考虑通过使用更多的内存,来换取更快的执行速度,即常说的空间换时间。不过,如果想要设计一个好的算法,还是需要两者兼顾的,在保证极低的时间成本下,尽可能的压缩空间成本

大O记号

渐进分析中,我们通常使用大O记号来表示分析的结果。不必过多的关注大O记号的由来,只需要记住大O记号为了刻画变化趋势和增长速度,可以忽略掉常数项和低次项
邓俊峰老师也给出了大O记号的结论:

在大O记号的意义下,函数各项正的常系数可以忽略并等同于1。多项式中的低次项均可忽略,只需保留最高次项。可以看出,大O记号的这些性质的确体现了对函数总体渐进增长趋势的关注和刻画。

我们不难看出,大O记号使用最高次项表示算法的复杂度,是一种对算法复杂度最坏情况的估算

大Ω记号和大Θ记号

除了大O记号外,用来表示算法复杂度的还有大Ω记号和大Θ记号,不过由于使用较少,我们在这里只引用邓俊峰老师的一句解释:

这里的称作“大Ω记号”(big-Ω notation)。与大O记号恰好相反,大Ω记号是对算法执行效率的乐观估计。

也就是说,大Ω记号是用来表示算法执行的最好情况的
大Ω记号和大O记号确定了算法复杂度的上下边界,那么有没有准确估计算法复杂度的记号呢?当然是有的,这种准确估计(就很矛盾)算法复杂度的表示方法称为大θ记号
不过在日常的计算中,我们更倾向于使用大O记号(人类都是悲观的),但是如果你遇到了大Ω记号和大θ记号,也要记得它们的含义。
好了,概念说了很多,下面我们来尝试计算一些渐进时间复杂度。

计算渐进时间复杂度

在我们了解了复杂度分析的概念和表示方法后,我们尝试着去计算几种常见的时间复杂度。

常数复杂度

常数复杂度是所有算法的终极梦想,因为这种复杂度代表着无论问题规模多大,都能在明确的时间内执行完成。
随便搞一段代码:

public int add(int a, int b) {
	int sum = a + b;
	return sum;
}

这段代码中,无论a和b输入什么,都只会执行3行代码,这种不随着输入规模而改变执行时间的就是常数级复杂度
大O记号中表示为: O ( 1 ) O(1) O(1)。无论执行几行,只要是能够确定的,都表示为 O ( 1 ) O(1) O(1)

线性复杂度

再搞一段代码:

public void add(int n) {
int result = 0;
for (int i = 0; i < n; i++) {
	result ++;
}
}

不难看出,这段代码总共会执行 ( 1 + 2 n ) (1+2n) (1+2n)行代码,那么执行时间也是 ( 1 + 2 n ) (1+2n) (1+2n)。根据大O记号中的结论,我们可以忽略掉所有的常数,得到的时间复杂度是 O ( n ) O(n) O(n)
事实上,2n和n的增长趋势是有一定差异的,但整体的变化趋势是随着n的增大而线性增大的,因此我们依旧可以忽略掉常数项和常数系数。
算法的复杂度分析,数据结构与算法,算法

平方复杂度

再再搞一段代码:

public void loop(int n) {
int result = 0;
for (int i = 0; i < n; i++) {
	result ++;
}

for (int i = 0; i < n; i++) {
	for (int j = 0; i < n; i++) {
		result ++;
	}
}
}

这段代码的执行次数也是一眼望穿,总共执行 ( 1 + 2 n + n + n 2 ) (1+2n+n+n^2) (1+2n+n+n2)行,执行时间也是 ( 1 + 2 n + n + n 2 ) (1+2n+n+n^2) (1+2n+n+n2)。合并后可以得到执行时间是 ( 1 + 3 n + n 2 ) (1+3n+n^2) (1+3n+n2),按照大O记号渐进时间复杂度是 O ( n 2 ) O(n^2) O(n2)
我们再来对比下低次项n对整体趋势的影响:
算法的复杂度分析,数据结构与算法,算法
可以看到,在这个级别的复杂度中,低次项n对整体趋势影响已经很小了,因此我们忽略掉低次项,对整体的变化趋势和增长速度影响非常小。

对数复杂度

再再再搞一段代码:

public void multiplication(int n) {
int result = 1;
while (result <= n) {
	result = result * 2;
}
}

可以尝试着计算这段代码的时间复杂度,这里需要用上一丢丢的高中数学知识。变量result每次的变化都是原来的2倍,我们可以得到每次循环中result的值如下:

  • 第1次: 2 0 2^0 20
  • 第2次: 2 1 2^1 21
  • 第3次: 2 2 2^2 22
  • 第X次: 2 x ≥   n 2x\geq n 2x n

那么我们只需要求解 2 x = n 2^x=n 2x=n中x的值即可获得这段代码的时间复杂度。在大O记号下,时间复杂度为 O ( log ⁡ _ n ) O(\log\_{}{n}) O(log_n)
我们通过一张函数图像,来看下对数复杂度的增长趋势:
算法的复杂度分析,数据结构与算法,算法

更多复杂度

以上是我们常见的时间复杂度。除此之外还有一些时间复杂度,我们将它们的函数曲线放到同一坐标系中感受下他们的变化趋势:
算法的复杂度分析,数据结构与算法,算法
可以看出,除了常数级时间复杂度外,对数级 O ( log ⁡ _ n ) O(\log\_{}{n}) O(log_n)也是非常理想的状态,这也是我们在设计算法是努力的方向。
最恐怖的是阶乘级复杂度。计算机领域中有一道著名的问题:旅行商问题,它的时间复杂度就是阶乘级的。另外旅行商问题也是NP完全问题。而由NP问题引发的P对NP问题是克雷数学研究所高额悬赏的七个”千禧年难题“之一。

最好,最坏和平均情况

这是今天的最后一段代码了:

public int main(int[] array, int target) {
	for(int i = 0; i < array.length; i++) {
		if(array[i] == target) {
			return i;
		}
	}
	return -1;
}

这段代码的逻辑很简单,循环查找数组中是否存在目标数字,如果存在就返回下标,不存在则返回 − 1 −1 1
如果target在首位,那么我们只需要执行一遍循环就可以查找到,此时的时间复杂度是 O ( 1 ) O(1) O(1)。如果target不在数组中,或者在数组的最后一位,那么需要遍历整个数组,此时的时间复杂度是 O ( n ) O(n) O(n)
这就是常说的最好情况和最坏情况。
接下来我们来了解下平均情况,还是先来看下邓俊峰老师的解释:

有时也需要考查所谓的平均情况(average case),也就是按照某种约定的概率分布,将规模为n的所有输入对应的计算时间加权平均。

在这段代码中,总共存在 ( n + 1 ) (n+1) (n+1) 种情况,其中n种情况是在数组中,1种情况是在数组外,假设每次循环代码的执行时间相同,根据每种情况的概率我们可以得到平均的执行时间为:
1 n + 1 + 2 n + 1 + 3 n + 1 + … + n − 1 n + 1 + n n + 1 + n + 1 n + 1 =   1 + 2 + 3 + … + ( n − 1 ) + n + ( n + 1 ) n + 1 = n 2 + x n + 1 2 n + 2 \frac{1}{n+1}+\frac{2}{n+1}+\frac{3}{n+1}+…+\frac{n-1}{n+1}+\frac{n}{n+1}+\frac{n+1}{n+1}= \frac{1+2+3+…+(n-1)+n+(n+1)}{n+1}=\frac{n^2+xn+1}{2n+2} n+11+n+12+n+13++n+1n1+n+1n+n+1n+1= n+11+2+3++(n1)+n+(n+1)=2n+2n2+xn+1
忽略掉所有常数项和常数系数后,我们得到:
n 2 + n n = 1 + n \frac{n^2+n}{n}={1+n} nn2+n=1+n
那么此时我们得到的时间复杂度就是平均情况的时间复杂度,大O记号为 O ( n ) O(n) O(n)

结语

今天的内容到这里就结束了,我们来回顾下都聊了哪些内容:
今天的主要内容是算法的复杂度分析,解释了算法复杂度分析渐进分析大O记号大Ω记号大θ记号,其中渐近分析和大O记号是数学概念引申到计算机领域的,因此会有一些数学证明,好在我们的算法和数学比起来还是很简单的,分析起来难度也不是很大。
然后计算了3种常见的渐进时间复杂度,并通过函数曲线展示了其余量级渐进复杂度的变化情况。

练习

最后是一道练习,来自邓俊峰老师的公开课《数据结构》复杂度分析的作业,如下:

x = n;
y = 1;
while(x >= (y-1)*(y-1)) {
	y++;
}

请计算以上程序的时间复杂度。


如果本文对你有帮助的话,还请多多点赞支持。如果文章中出现任何错误,还请批评指正。最后欢迎大家关注分享硬核Java技术的金融摸鱼侠[王有志](https://www.yuque.com/wangyouzhi-u3woi/dfhnl0/hqrch62un0cc9sp2?singleDoc# 《🔥快来关注我》),我们下次再见!文章来源地址https://www.toymoban.com/news/detail-817012.html

到了这里,关于算法的复杂度分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构与算法——TypeScript】算法的复杂度分析、 数组和链表的对比

    什么是算法复杂度(现实案例)? ❤️‍🔥 前面已经解释了什么是算法? 其实就是解决问题的一系列步骤操作、逻辑。 ✅ 对于同一个问题,我们往往有很多种解决思路和方法,也就是可以采用不同的算法。 举个例子(现实例子):在一个庞大的图书馆中,我们需要找一本书

    2024年02月14日
    浏览(50)
  • 扎实打牢数据结构算法根基,从此不怕算法面试系列之007 week01 02-07 简单的复杂度分析

    复杂度分析本身是非常理论化的一个内容,在计算机科学中,有一个专门的学科叫做—— 计算复杂性理论 。 很多童鞋看过《算法导论》,这本书的内容很多很强调算法导论。 但是实际上,对于普通程序员来说, 不需要 过度强调理论化的内容 。因为工作中更多面对的是实际

    2023年04月19日
    浏览(56)
  • 数据结构:算法(特性,时间复杂度,空间复杂度)

    算法(Algorithm)是对 特定问题求解步骤 的一种描述,它是指令的有限序列,其中的每条指令表示一个或多个操作。 一个算法必须总在执行有穷步之后结束,且每一步都可在有穷时间内完成。 算法必须是有穷的,而程序可以是无穷的 算法中每条指令必须有确切的含义,对于

    2024年02月06日
    浏览(52)
  • 算法的时间复杂度和空间复杂度(数据结构)

    目录 1、算法效率 1如何衡量一个算法的好坏 2算法的复杂度 2、时间复杂度 1时间复杂度的概念 2大O的渐进表示法 2时间复杂度计算例题 1、计算Func2的时间复杂度 2、计算Func3的时间复杂度 3、计算Func4的时间复杂度 4、计算strchr的时间复杂度 5、计算BubbleSort的时间复杂度 6、计算

    2024年02月03日
    浏览(67)
  • 【数据结构和算法】时间复杂度和空间复杂度

    目录   一、前言 二、时间复杂度 2.1时间复杂度表示形式 2.1.1规则: 3.1如何计算时间复杂度 3.1.1线性阶 3.1.2平方阶 3.1.3对数阶 常见的时间复杂度排序: 三、空间复杂度 3.1Java的基本类型内存占用 数据结构和算法是程序的灵魂,这是某位程序员大佬所言,学习了这门,我们便可

    2023年04月09日
    浏览(44)
  • 数据结构与算法—时间复杂度和空间复杂度

    目录 1、什么是数据结构? 2、什么是算法? 3、算法的复杂度 4、时间复杂度 (1) 时间复杂度的概念:  (2) 大O的渐进表示法:  六个例题: (3) 时间复杂度对比:  三个例题:  OJ题分析时间复杂度 5、空间复杂度 (1)常见复杂度对比  (2)OJ题分析空间复杂度 小结 数据结构 (D

    2024年02月07日
    浏览(86)
  • 数据结构--算法的时间复杂度和空间复杂度

    算法效率是指 算法在计算机上运行时所消耗的时间和资源 。这是衡量算法执行速度和资源利用情况的重要指标。 例子: 这是一个斐波那契函数,用的是递归的计算方法,每次创建函数就会在栈区开辟一块空间,递归次数越多,开辟空间越多; 所以, 代码的简洁说明不了算

    2024年02月15日
    浏览(43)
  • 数据结构与算法-时间复杂度与空间复杂度

    数据结构是计算机存储、组织数据的方式,指相互之间存在一种或多种特定关系的数据元素的集合。 算法就是定义良好的计算过程,他取一个或一组的值为输入,并产生一个或一组值作为输出。简单来说算法就是一系列的计算步骤,用来将输入数据转化成输出结果。 算法在

    2024年02月07日
    浏览(47)
  • 【数据结构与算法】1.时间复杂度和空间复杂度

    📚博客主页:爱敲代码的小杨. ✨专栏:《Java SE语法》 ❤️感谢大家点赞👍🏻收藏⭐评论✍🏻,您的三连就是我持续更新的动力❤️ 🙏小杨水平有限,欢迎各位大佬指点,相互学习进步! 算法效率分为两种:第一种是时间效率;第二种是空间效率。时间效率又称为时间

    2024年01月20日
    浏览(49)
  • 【数据结构与算法篇】时间复杂度与空间复杂度

       目录 一、数据结构和算法 1.什么是数据结构?  2.什么是算法? 3.数据结构和算法的重要性 二、算法的时间复杂度和空间复杂度 1.算法效率 2.算法的复杂度 3.复杂度在校招中的考察 4.时间复杂度 5.空间复杂度  6.常见复杂度对比 7.复杂度的OJ练习   👻内容专栏:《数据结

    2023年04月24日
    浏览(60)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包