何谓数据结构数据结构是在整个计算机科学与技术领域上广泛被使用的术语。它用来反映一个数据的内部构成,即一个数据由那些成分数据构成,以什么方式构成,呈什么结构。数据结构有逻辑上的数据结构和物理上的数据结构之分。逻辑上的数据结构反映成分数据之间的逻辑关系,而物理上的数据结构反映成分数据在计算机内部的存储安排。数据结构是数据存在的形式。数据结构是信息的一种组织方式,其目的是为了提高算法的效率,它通常与一组算法的集合相对应,通过这组算法集合可以对数据结构中的数据进行某种操作。数据结构主要研究什么?数据结构作为一门学科主要研究数据的各种逻辑结构和存储结构,以及对数据的各种操作。因此,主要有三个方面的内容:数据的逻辑结构;数据的物理存储结构;对数据的操作(或算法)。通常,算法的设计取决于数据的逻辑结构,算法的实现取决于数据的物理存储结构。什么是数据结构?什么是逻辑结构和物理结构?数据是指由有限的符号(比如,0和1,具有其自己的结构、操作、和相应的语义)组成的元素的集合。结构是元素之间的关系的集合。通常来说,一个数据结构DS可以表示为一个二元组:DS=(D,S),//i.e.,data-structure=(data-part,logic-structure-part)这里D是数据元素的集合(或者是“结点”,可能还含有“数据项”或“数据域”),S是定义在D(或其他集合)上的关系的集合,S={R|R:D×D×...},称之为元素的逻辑结构。逻辑结构有四种基本类型:集合结构、线性结构、树状结构和网络结构。表和树是最常用的两种高效数据结构,许多高效的算法可以用这两种数据结构来设计实现。表是线性结构的(全序关系),树(偏序或层次关系)和图(局部有序(weak/localorders))是非线性结构。数据结构的物理结构是指逻辑结构的存储镜像(image)。数据结构DS的物理结构P对应于从DS的数据元素到存储区M(维护着逻辑结构S)的一个映射:P:(D,S)--M存储器模型:一个存储器M是一系列固定大小的存储单元,每个单元U有一个唯一的地址A(U),该地址被连续地编码。每个单元U有一个唯一的后继单元U'=succ(U)。P的四种基本映射模型:顺序(sequential)、链接(linked)、索引(indexed)和散列(hashing)映射。因此,我们至少可以得到4×4种可能的物理数据结构:sequential(sets)linkedlistsindexedtreeshashgraphs(并不是所有的可能组合都合理)数据结构DS上的操作:所有的定义在DS上的操作在改变数据元素(节点)或节点的域时必须保持DS的逻辑和物理结构。DS上的基本操作:任何其他对DS的高级操作都可以用这些基本操作来实现。最好将DS和他的所有基本操作看作一个整体——称之为模块。我们可以进一步将该模块抽象为数据类型(其中DS的存储结构被表示为私有成员,基本操作被表示为公共方法),称之为ADT。作为ADT,堆栈和队列都是一种特殊的表,他们拥有表的操作的子集。对于DATs的高级操作可以被设计为(不封装的)算法,利用基本操作对DS进行处理。好的和坏的DS:如果一个DS可以通过某种“线性规则”被转化为线性的DS(例如线性表),则称它为好的DS。好的DS通常对应于好的(高效的)算法。这是由计算机的计算能力决定的,因为计算机本质上只能存取逻辑连续的内存单元,因此如何没有线性化的结构逻辑上是不可计算的。比如对一个图进行操作,要访问图的所有结点,则必须按照某种顺序来依次访问所有节点(要形成一个偏序),必须通过某种方式将图固有的非线性结构转化为线性结构才能对图进行操作。树是好的DS——它有非常简单而高效的线性化规则,因此可以利用树设计出许多非常高效的算法。树的实现和使用都很简单,但可以解决大量特殊的复杂问题,因此树是实际编程中最重要和最有用的一种数据结构。树的结构本质上有递归的性质——每一个叶节点可以被一棵子树所替代,反之亦然。实际上,每一种递归的结构都可以被转化为(或等价于)树形结构。计算机中数据的描述方式我们知道,数据可以用不同的形式进行描述或存储在计算机存储器中。最常见的数据描述方法有:公式化描述、链接描述、间接寻址和模拟指针。公式化描述借助数学公式来确定元素表中的每个元素分别存储在何处,也就通过公式计算元素的存储器地址。最简单的情形就是把所有元素依次连续存储在一片连续的存储空间中,这就是通常所说的连续线性表,即数组。复杂一点的情形是利用复杂的函数关系根据元素的某些特征来计算元素在内存中的位置,这种技术称为散列技术(Hash,经常音译为哈希技术)。在链接描述中,元素表中的每个元素可以存储在存储器的不同区域中,每个元素都包含一个指向下一个元素的指针。这就是通常所说的链表。这种描述方法的好处是,知道了第一个元素的位置,就可以依次找到第n个元素的位置,而且在其中插入元素非常方便,缺点是查找某个元素要遍历所有在该元素之前的元素,实际应用中经常和公式化描述结合起来使用。在间接寻址方式中,元素表中的每个元素也可以存储在存储器的不同区域中,不同的是,此时必须保存一张表,该表的第i项指向元素表中的第i个元素,所以这张表是一个用来存储元素地址的表。指针数组(元素为指针的数组)就是这种描述法的应用。这种描述方法是公式化描述和链接描述的一种折衷方案,同时具有两种描述方法的优点,但是需要额外的内存开销。模拟指针非常类似于链接描述,区别在于它用整数代替了指针,整数所扮演的角色与指针所扮演的角色完全相同。模拟指针的描述方式是链接描述和公式化描述的结合,元素被存储在不同的区域中,每个元素包含一个指示下一个元素位置的整数,可以通过某种公式由该整数计算出下一个元素的存储器地址。线性表的游标实现就是模拟指针描述法。算法Algorithm算法是在有限步骤内求解某一问题所使用的一组定义明确的规则。通俗点说,就是计算机解题的过程。在这个过程中,无论是形成解题思路还是编写程序,都是在实施某种算法。前者是推理实现的算法,后者是操作实现的算法。一个算法应该具有以下五个重要的特征:1.有穷性:一个算法必须保证执行有限步之后结束;2.确切性:算法的每一步骤必须有确切的定义;3.输入:一个算法有0个或多个输入,以刻画运算对象的初始情况,所谓0个输入是指算法本身定除了初始条件;4.输出:一个算法有一个或多个输出,以反映对输入数据加工后的结果。没有输出的算法是毫无意义的;5.可行性:算法原则上能够精确地运行,而且人们用笔和纸做有限次运算后即可完成。DidyouknowAlgorithm一词的由来Algorithm(算法)一词本身就十分有趣。初看起来,这个词好像是某人打算要写“Logarithm”(对数)一词但却把头四个字母写的前后颠倒了。这个词一直到1957年之前在Webster'sNewWorldDictionary(《韦氏新世界词典》)中还未出现,我们只能找到带有它的古代涵义的较老形式的“Algorism”(算术),指的是用阿拉伯数字进行算术运算的过程。在中世纪时,珠算家用算盘进行计算,而算术家用算术进行计算。中世纪之后,对这个词的起源已经拿不准了,早期的语言学家试图推断它的来历,认为它是从把algiros(费力的)+arithmos(数字)组合起来派生而成的,但另一些人则不同意这种说法,认为这个词是从“喀斯迪尔国王Algor”派生而来的。最后,数学史学家发现了algorism(算术)一词的真实起源:它来源于著名的PersianTextbook(《波斯教科书》)的作者的名字AbuJa'farMohammedibnMûsâal-Khowârizm(约公元前825年)——从字面上看,这个名字的意思是“Ja'far的父亲,Mohammed和Mûsâ的儿子,Khowârizm的本地人”。Khowârizm是前苏联XИBA(基发)的小城镇。Al-Khowârizm写了著名的书Kitabaljabrw'al-muqabala(《复原和化简的规则》);另一个词,“algebra”(代数),是从他的书的标题引出来的,尽管这本书实际上根本不是讲代数的。逐渐地,“algorism”的形式和意义就变得面目全非了。如牛津英语字典所说明的,这个词是由于同arithmetic(算术)相混淆而形成的错拼词。由algorism又变成algorithm。一本早期的德文数学词典VollstandigesMathematischesLexicon(《数学大全辞典》),给出了Algorithmus(算法)一词的如下定义:“在这个名称之下,组合了四种类型的算术计算的概念,即加法、乘法、减法、除法”。拉顶短语algorithmusinfinitesimalis(无限小方法),在当时就用来表示Leibnitz(莱布尼兹)所发明的以无限小量进行计算的微积分方法。1950年左右,algorithm一词经常地同欧几里德算法(Euclid'salgorithm)联系在一起。这个算法就是在欧几里德的《几何原本》(Euclid'sElements,第VII卷,命题i和ii)中所阐述的求两个数的最大公约数的过程(即辗转相除法)。伪代码的使用UsageofPseudocode伪代码(Pseudocode)是一种算法描述语言。使用为代码的目的是为了使被描述的算法可以容易地以任何一种编程语言(Pascal,C,Java,etc)实现。因此,伪代码必须结构清晰,代码简单,可读性好,并且类似自然语言。下面介绍一种类Pascal语言的伪代码的语法规则。伪代码的语法规则1.在伪代码中,每一条指令占一行(elseif例外,),指令后不跟任何符号(Pascal和C中语句要以分号结尾);2.书写上的“缩进”表示程序中的分支程序结构。这种缩进风格也适用于if-then-else语句。用缩进取代传统Pascal中的begin和end语句来表示程序的块结构可以大大提高代码的清晰性;同一模块的语句有相同的缩进量,次一级模块的语句相对与其父级模块的语句缩进;例如:line1line2subline1subline2subsubline1subsubline2subline3line3而在Pascal中这种关系用begin和end的嵌套来表示,line1line2beginsubline1subline2beginsubsubline1subsubline2end;subline3end;line3在C中这种关系用{和}的嵌套来表示,line1line2{subline1subline2{subsubline1subsubline2}subline3}line33.在伪代码中,通常用连续的数字或字母来标示同一即模块中的连续语句,有时也可省略标号。例如:1.line12.line2a.subline1b.subline21.subsubline12.subsubline2c.subline33.line34.符号△后的内容表示注释;5.在伪代码中,变量名和保留字不区分大小写,这一点和Pascal相同,与C或C++不同;6.在伪代码中,变量不需声明,但变量局部于特定过程,不能不加显示的说明就使用全局变量;7.赋值语句用符号←表示,x←exp表示将exp的值赋给x,其中x是一个变量,exp是一个与x同类型的变量或表达式(该表达式的结果与x同类型);多重赋值i←j←e是将表达式e的值赋给变量i和j,这种表示与j←e和i←e等价。例如:x←yx←20*(y+1)x←y←30以上语句用Pascal分别表示为:x:=y;x:=20*(y+1);x:=30;y:=30;以上语句用C分别表示为:x=y;x=20*(y+1);x=y=30;8.选择语句用if-then-else来表示,并且这种if-then-else可以嵌套,与Pascal中的if-then-else没有什么区别。例如:if(Condition1)then[Block1]elseif(Condition2)then[Block2]else[Block3]9.循环语