「算法分析」的目的在于改进算法。
正如上文中所提到的那样:算法所追求的就是 所需运行时间更少(时间复杂度更低)、占用内存空间更小(空间复杂度更低)。所以进行「算法分析」,就是从运行时间情况、空间使用情况两方面对算法进行分析。
不论哪一种测量角度
为了比较不同算法方案的好坏、优劣,我们需要一些监测指标
有两种检测、测量方法
- 事后统计:将两个算法各编写一个可执行程序,交给计算机执行,记录下各自的运行时间和占用存储空间的实际大小,从中挑选出最好的算法。
- 预先估算:在算法设计出来之后,根据算法中包含的步骤,估算出算法的运行时间和占用空间。比较两个算法的估算值,从中挑选出最好的算法。
大多数情况下,我们会选择第 2 种方式。因为第 1 种方式的工作量实在太大,得不偿失。另外,即便是同一个算法,用不同的语言实现,在不同的计算机上运行,所需要的运行时间都不尽相同。所以我们一般采用预先估算的方法来衡量算法的好坏。
所以测量方法就是不具体运行该算法,就是提前估计
怎么估计?
首先
采用预先估算的方式下,编译语言、计算机运行速度都不是我们所考虑的对象。我们只关心随着问题规模 n 扩大时,时间开销、空间开销的增长情况。
这里的 「问题规模 n」 指的是:算法问题输入的数据量大小。对于不同的算法,定义也不相同。
- 排序算法中:n表示需要排序的元素数量。
- 查找算法中:n表示查找范围内的元素总数:比如数组大小、二维矩阵大小、字符串长度、二叉树节点数、图的节点数、图的边界点等。
- 二进制计算相关算法中:n 表示二进制的展开宽度。
接下来,我们将具体讲解「时间复杂度」和「空间复杂度」。
我们先从时间复杂度开始
时间复杂度:
基本操作次数是实践复杂度的度量方式、度量标准
我的个人理解:算法运行所需要花费的时间和算法中执行基本操作的次数量是成正比的
那什么是基本操作
基本操作的定义:
算法执行中的每一条语句
一条语句的运行时间不受输入规模n的影响
运行时间基本是固定的
基本操作的一个重要属性:
每一条语句的执行时间是固定的,是一个常数
换句话说,可以在常数时间内完成执行
换句话说,基本操作的运行时间是固定的。
备注:
基本操作的一个非常好的解释和介绍:
这句话的意思是,基本操作的执行时间是固定的,不随着操作数的增加而增加。换句话说,无论输入规模如何变化,这些基本操作的执行时间都是恒定的,不受输入规模的影响。
举个例子来解释:假设有一个算法用于对一个长度为n的数组中的元素求和。基本操作可能是对数组中的每个元素进行一次访问,并将其加到总和中。在这个例子中,基本操作是访问数组元素并进行加法运算。即使数组的长度n增加,每个元素的访问和加法运算的时间仍然是固定的,不受n的影响。因此,这些基本操作的执行时间不依赖于操作数(即数组中的元素个数),而是固定的。
另一个例子是对两个整数进行相加的操作。无论这两个整数的大小如何变化,对它们进行相加的时间是固定的,因为这是一个基本操作,其执行时间不取决于整数的具体值或位数。
因此,基本操作是指在算法中执行的一些简单、基本的操作,其执行时间是固定的,不受操作数的影响。
基本操作次数是基于基本操作的概念之上的概念
算法运行所需要花费的时间是基于基本操作次数来计算的
数组的长度n就是问题的输入规模n,
基本操作次数又是由 数组的长度n,就是问题的输入规模n 决定的
(例子:如果两个数的规模很大,相加操作依赖于两个数的位数,则两个数的相加操作不是一个基本操作,而每一位数的相加操作才是一个基本操作。有n个位数,则要有n个基本操作次数)
而算法执行时间的增长趋势如果跟基本操作次数的增长趋势相同,那这是一个好的现象,是我们希望看到的现象
现在要引入一个新的概念:
2.2 渐进符号
渐进符号(Asymptotic Symbol):用来刻画、描述函数的增长速度,只保留增长速度最快的那一项,因为当问题的输入规模n很大时,其他项的贡献都不会太显著,尤其在判断大的趋势、方向的时候,我们可以忽略不计。
关键词:
基本操作的执行时间
基本操作的操作数
算法中的每一行或者每一条语句
基本操作
运行时间
举个基本操作的例子:
比如两个整数相加的操作,如果两个数的规模不大,运行时间不依赖于整数的位数,则相加操作就可以看做是基本操作。
数组的概念:在物理层面连续的内存空间上,存储同类型的数据
数组的特点:
可以随机访问,也就是根据数组的下标,直接定位到下标指向的那个元素的物理上在内存的存放位置。