第一讲
什么是数据结构
- 如何摆放图书馆的书???
摆放图书馆的书与书的规模和对书要进行的操作有关。
也就是说,如何确定一个问题的数据结构,需要考虑到问题的规模和程序中要进行的操作。
- 打印一个数列从1到N,采用递归和循环的方式。
递归方式容易理解,解决问题效率高,但计算执行复杂,占用空间大。循环的方式不便于理解,但计算机执行效率高,占用内存小。
说明解决问题的效率和空间占用效率有关
- 一个多项式求值的问题。
对于一个多项式求值的问题,可以采用正常的从前往后逐项累加,也可以使用“巧妙的结合律”,从里向外求和,从而使得乘法的次数被降低,算法执行效率提升。
说明了解决问题的效率和算法的巧妙程度有关
综上,要谈论一个问题采用什么数据结构必须要明确问题的规模和问题的需求,这样才能使用合适的数据结构。我们可以使用“空间”来换取“时间”,或者用“时间”换取“空间”。算法的巧妙程度决定了一个算法的效率。
数据组织的逻辑结构:一对一(线性),一对多(树),多对多(图)
数据结构的抽象描述
- 数据对象集
- 在数据集上对应的操作
什么是算法
- 一个有限的指令集
- 接受一些输入。可以没有输入。
- 产生输出。
- 有穷性。一定步骤后终止。
- 确定性。每一步都应该是明确的,没有歧义。
- 可行性。
- 每一天指令应该:
- 正确性。
- 可读性。便于阅读理解和维护。
- 健壮性。 输入不合法时,依然不会崩溃。
什么是好的算法
衡量一个算法的好坏,普遍会使用时间复杂度和空间复杂度。它们两个共同决定了算法所需要消耗的资源。这些资源可能包括CPU资源,RAM资源,带宽资源。
确定一个算法的好坏可以使用事后统计或者事前分析。事后统计就是利用设计好的数据集,在测试程序上运行,然后统计各种资源的使用情况,从而分析算法的效率。事前分析就是利用统计的方法,对算法效率进行估算。
时间复杂度
算法的时间复杂度,描述的是算法的运行时间。实际的运行时间其实是很难计算出来,对于不同的环境,每条语句的执行时间可能都不相同。这里的描述相当于算法中语句的执行次数。
对于给定的问题规模n,那么语句的执行次数和问题规模n之间的关系可以用T(n)来表示。通过T的大致趋势也能反映出算法的时间耗费,不需要给出精确地T值,所以在这个基础上引出来渐进时间复杂度。
当问题规模n趋向于无穷大时,算法的时间复杂度T(n)的渐进上界,叫做渐进时间复杂度。在实际使用中,渐进时间复杂度和时间复杂度不予区分,记为:T(n) = O(f(n)).在实际中,O表示在最坏情况下的渐进上界。关于渐进符号的知识,可以参考这里。
然而,对于同一个算法,不同的数据,可能时间复杂度是不同的。所以,又分为三种情况的时间复杂度:
- 最坏情况:任意输入最长的运行时间。
- 平均情况:任意输入期待运行时间。
- 最佳情况:对于特定输入,最快执行时间。
在实际中,一般使用最坏情况下的时间复杂度。
空间复杂度
空间复杂度用S表示。它描述的是算法执行期间所要占用的空间。程序占用的空间一部分是和算法无关的,例如:指令,常数,输入数据等,所以这一部分不是分析的空间。在算法执行过程中所需要的辅助空间才是我们所关心的。