天下至深是学海,世间极顶是书山❗❗❗
《数据结构与算法》阅读笔记,强烈建议购买此书反复学习
坚持每天阅读习惯,总有一日量变为质变,最终通向成功✌️
这几天会先整理以前的笔记,加油
️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️
算法是对特定问题求解过程的描述,是指令的有限序列,即为解决某一特定问题而采取的具体而有限的操作步骤。程序是算法的一种实现,计算机按照程序逐步执行算法,实现对问题的求解。
NOTE:
算法具有以下性质:
⭐设计算法时,应该关注算法的结束条件。
算法设计与算法分析是计算机科学的核心问题。
常用的算法设计方法有:
⭐算法的标准
解决一个问题的方法可能有很多,但能称得上算法的,首先他必须能彻底解决这个问题(称为准确性),且根据其编写出的程序在任何情况下都不能崩溃(称为健壮性)。
在满足准确性和健壮性的基础上,还有一个重要的筛选条件,即通过算法所编写出的程序的运行效率。程序的运行效率具体可以从2哥方面衡量,分别为:
根据算法编写出的程序,运行时间更短,运行期间占用的内存更少,该算法的运行效率更高,算法也就越好。
在数据结构中,用时间复杂度来衡量程序运行时间的多少;用空间复杂度来衡量程序运行所需内存空间的大小。
⭐时间复杂度
判断一个算法所需程序运行时间的多少,并不是将程序编写出来,通过在计算机运行所消耗的时间来度量。一方面,解决一个问题的算法可能有很多种——实现的工作量无疑是巨大的,得不偿失;另一方面,不同计算机的软、硬件环境不同,即便使用同一台计算机,不同时间段其系统环境也不相同,程序的运行时间很可能会受影响,严重时甚至会导致误判。
实际情况下,我们更喜欢用一个估值来表示算法所编程序的运行时间。
NOTE:虽然估值无法准确的表达算法所编程序的运行时间,但它的由来并没凭空揣测,需要通过缜密的计算后才能得出。
表示一个算法所编程序运行时间的多少,用的不是准确值(事实上也无法得出),而是根据合理方法得到的预估值。
如何预估:先分别计算按程序中每条语句的执行次数,然后用总的执行次数间接表示程序运行的时间。
代码举例:
for(int i =0; i <n ;i++) // 从 0 到 n,执行 n+1 次
{
a++; // 从 0 到 n-1,执行 n 次
}
可以看到,这段程序中仅有 2 行代码,其中:
因此,整段代码中所有语句共执行了 (n+1)+n 次,即 2n+1 次。数据结构中,每条语句的执行次数,又被称为该语句的频度。整段代码的总执行次数,即整段代码的频度。
另一个例子:
for(int i =0;i <n ;i++) // n+1
{
for(int j =0;j <m ;j++) // n*(m+1)
{
num++; // n*m
}
}
计算此段程序的频度为:(n+1)+n*(m+1)+nm,简化后得 2nm+2n+1。当n,m逐渐增大时,完全可以认为n==m,可以简化为2n^2+2n+1。
在数据结构中,频度表达式可以这样简化:
事实上,对于一个算法(或者一段程序)来说,其最简频度往往就是最深层次的循环结构中某一条语句的执行次数。
⭐大 O 记法:
常用的几种时间复杂度,以及它们之间的大小关系:
O(1)常数阶 < O(logn)对数阶 < O(n)线性阶 < O(n^2)平方阶 < O(n^3)(立方阶) < O(2^n) (指数阶)
NOTE:这里仅介绍了以最坏情况下的频度作为时间复杂度,而在某些实际场景中,还可以用最好情况下的频度和最坏情况下的频度的平均值来作为算法的时间复杂度。
空间复杂度
和时间复杂度类似,一个算法的空间复杂度,也常用大 O 记法表示。
要知道每一个算法所编写的程序,运行过程中都需要占用大小不等的存储空间,例如:
首先,程序自身所占用的存储空间取决于其包含的代码量,如果要压缩这部分存储空间,就要求我们在实现功能的同时,尽可能编写足够短的代码。
程序运行过程中输入输出的数据,往往由要解决的问题而定,即便所用算法不同,程序输入输出所占用的存储空间也是相近的。
事实上,对算法的空间复杂度影响最大的,往往是程序运行过程中所申请的临时存储空间。不同的算法所编写出的程序,其运行时申请的临时存储空间通常会有较大不同。
所以,如果程序所占用的存储空间和输入值无关,则该程序的空间复杂度就为 O(1);反之,如果有关,则需要进一步判断它们之间的关系:
NOTE:在多数场景中,一个好的算法往往更注重的是时间复杂度的比较,而空间复杂度只要在一个合理的范围内就可以。