数据结构的创始人:
1938年12月7日,Donald E. Knuth 出生于美国威斯康星州密尔沃基市。其父是个中学教师,经常在星期天到教堂演奏管风琴,小 Knuth 耳濡目染,日后也成为教师,业余爱好也是弹管风琴。1938年12月7日,Donald E. Knuth 出生于美国威斯康星州密尔沃基市。其父是个中学教师,经常在星期天到教堂演奏管风琴,小 Knuth 耳濡目染,日后也成为教师,业余爱好也是弹管风琴。
1. 数据结构的组成
数据结构的最基本组成可以使用二元数组表示,即data_structure={D,R},其中,D为数据元素的集合,R为根据数据间关系形成的结构的集合。我们也可以采用四元数组表示一种数据结构,即data_structure={D,L,S,O},相当于我们将数据结构分成逻辑结构和存储结构两部分,并规定了可对数据进行的运算。
1.1 逻辑结构
逻辑结构即数据之间的“关系”,通常有线性结构、树形结构、图形结构和集合结构。
逻辑结构 特点
线性结构 一对一
树形结构 一对多
图形结构 多对多
集合结构 松散
1.2 存储结构
存储结构 特点
顺序存储 通常使用数组,地址计算L(i)=L0+(i-1)*m,访问速度快,查找快。
链式存储 元素结构之间无需直接相邻,每个结点存储了data和point(指向下一个元素的地址),节约空间且灵活。
对数据元素的基本操作:查找、插入、删除、遍历和排序。
2.数据结构的算法及算法分析
算法是利用计算机解决问题的处理步骤,简而言之,算法就是解决问题的步骤。
算法不仅仅用于计算机的数据处理,现实世界中的各种问题也需要结合算法的概念来解决,
学习算法的同时能提高自己的编程能力。一个好的算法是编写程序的模型,因为它能创造计算机程序,其中还包含了程序的精髓。学过算法的人写出的程序和没学过算法的人写出的程序有明显的差距。要写出既能正确执行又能提高效率的好程序,算法的学习是不可或缺的。
算法的学习类似于学习游戏攻略,有了攻略就能轻松取得游戏的成功。
输入 输出 有穷性 确定性 可行性
有效性:算法必须要为给定的任务给出正确的结果,即,有满足条件的输入值时,此算法一定要保证正常工作(返回正确的输出值)。表明算法有效性的方法之一就是断点。断点设置在算法的任意位置上,判断此位置是否满足给出的条件,即,程序是否正确运行。
终止性:算法中没有永远反复执行,即,没有无限循环,且不返回答案的情况。算法终止性可以用反复处理结束条件的判断变量,或经过有限次的反复一定能到达结束条件等方法证明。
用计算机编写程序时,为了提高应用程序的效率,把设计上的错误最小化,有一种编程思想叫做结构化程序设计。
结构化程序设计中所有的处理流程,可以用以下三种结构组合而成:
顺序结构:按照所述顺序处理
选择结构:根据判断条件改变执行流程
循环结构:当条件成立时,反复执行给定的处理操作
求最大公约数的代码
int CommonFactor(int m,int n)
{
r=m%n;
while(r!=0)
{
m=n;
r=m%n;
}
return 0;
}
2-5. 算法的分析
算法的时间复杂度
(1)语句频度T(n): 一个算法执行所花费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能对每个算法都上机测试,只需知道哪个算法花费的时间多,哪个算法花费的时间少就可以了。而且一个算法花费的时间与算法中的基本操作语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度,记为T(n)。
(2)时间复杂度: 在刚才提到的语句频度中,n称为问题的规模,当n不断变化时,语句频度T(n)也会不断变化。但有时我们想知道它的变化呈现什么规律。为此,我们引入时间复杂度概念。 一般情况下,算法中的基本操作语句的重复执行次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n) / f(n) 的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作 T(n)=O( f(n) ),称O( f(n) ) 为算法的渐进时间复杂度,简称时间复杂度。
T(n) 不同,但时间复杂度可能相同。 如:T(n)=n²+5n+6 与 T(n)=3n²+3n+2 它们的T(n) 不同,但时间复杂度相同,都为O(n²)。
(3)常见的时间复杂度有:常数阶O(1),对数阶O(log2n),线性阶O(n),线性对数阶O(nlog2n),平方阶O(n2),立方阶O(n3), k次方阶O(nk),指数阶O(2n)
算法的空间复杂度
空间复杂度(Space Complexity) 是对一个算法在运行过程中临时占用存储空间大小的量度,记做 S(n)=O(f(n)) ,其中n为问题的规模。利用算法的空间复杂度,可以对算法的运行所需要的内存空间有个预先估计。
一个算法执行时除了需要存储本身所使用的指令、常数、变量和输入数据外,还需要一些对数据进行操作的工作单元和存储一些计算所需的辅助空间。算法执行时所需的存储空间包括以下两部分。
(1)固定部分。这部分空间的大小与输入/输出的数据的个数、数值无关。主要包括指令空间(即代码空间)、数据空间(常量、简单变量)等所占的空间。这部分属于静态空间。
(2)可变空间,这部分空间的主要包括动态分配的空间,以及递归栈所需的空间等。这部分的空间大小与算法有关。
举例分析算法的空间复杂度:
public void reserse(int[] a, int[] b) {
int n = a.length;
for (int i = 0; i < n; i++) {
b[i] = a[n - 1 - i];
}
}
上方的代码中,当程序调用 reserse() 方法时,要分配的内存空间包括:引用a、引用b、局部变量n、局部变量i
因此 f(n)=4 ,4为常量。所以该算法的空间复杂度 S(n)=O(1)