数据结构与算法.docx
《数据结构与算法.docx》由会员分享,可在线阅读,更多相关《数据结构与算法.docx(51页珍藏版)》请在冰豆网上搜索。
数据结构与算法
1数据结构与算法
1.1算法
1.1.1.1算法的基本特征
一般来说,一个算法应具有以下4个基本特征。
(1)可行性(Effectiveness):
算法在特定的执行环境中执行,应当能够得出满意的结果,即必须有一个或多个输出。
(2)确定性(Definiteness):
算法中的每一个步骤都必须有明确的定义,不允许有模棱两可的解释和多义性。
(3)有穷性(Finiteness):
算法必需在有限时间内做完,即算法必需能在执行有限个步骤之后终止。
(4)拥有足够的情报:
要使算法有效必需为算法提供足够的情报。
当算法拥有足够的情报时,此算法才是有效的;而当提供的情报不够时,算法可能无效。
1.1.2.1算法的时间复杂度
算法的时间复杂度是指执行算法所需要的计算工作量。
为了能够比较客观地反映出一个算法的效率,在度量一个算法的工作量时,不仅应该与所使用的计算机、程序设计语言以及程序编制者无关,而且还应该与算法实现过程中的许多细节无关。
算法的计算工作量是用算法所执行的基本运算次数来度量的,而算法所执行的基本运算次数是问题规模(通常用整数n表示)的函数。
即
算法的工作量=f(n)
例如,在N×N矩阵相乘的算法中,整个算法的执行时间与该基本操作(乘法)重复执行的次数n3成正比,也就是时间复杂度为n3,即
f(n)=O(n3)
在有的情况下,算法中的基本操作重复执行的次数还随问题的输入数据集不同而不同。
例如在起泡排序的算法中,当要排序的数组a初始序列为自小至大有序时,基本操作的执行次数为0;当初始序列为自大至小有序时,基本操作的执行次数为n(n-1)/2。
对这类算法,可以采用平均性态和最坏情况复杂性两种方法来分析。
1.1.2.2算法的空间复杂度
算法的空间复杂度是指执行这个算法所需要的内存空间。
一个算法所占用的存储空间包括算法程序所占的空间、输入的初始数据所占的存储空间以及算法执行过程中所需要的额外空间。
其中额外空间包括算法程序执行过程中的工作单元以及某种数据结构所需要的附加存储空间。
如果额外空间量相对于问题规模来说是常数,则称该算法是原地(inplace)工作的。
在许多实际问题中,为了减少算法所占的存储空间,通常采用压缩存储技术,以便尽量减少不必要的额外空间。
1.2基本概念
1.2.1数据结构的定义
数据结构是计算机科学与技术领域广泛使用的一个基本术语,用来反映数据的内部构成。
在给出数据结构的定义之前,我们先弄清楚几个概念。
数据(data):
是对客观事物的符号表示,在计算机科学中是指所有能输入到计算机中并被计算机程序处理的符号的总称。
数据元素(dataelement):
是数据的基本单位,在计算机程序中通常作为一个整体进行考虑和处理。
数据对象(dataobject):
是性质相同的数据元素的集合,是数据的一个子集。
简单地说,数据结构是指相互关联的数据元素的集合,即数据的组织形式。
所谓结构,就是指数据元素之间的前后件关系(或称直接前驱与直接后继关系)。
例如,在考虑一日三餐的时间顺序关系时,"早餐"是"午餐"的前件(或直接前驱),而"午餐"是"早餐"的后件(或直接后继);同样,"午餐"是"晚餐"的前件,"晚餐"是"午餐"的后件。
又例如,在考虑学历的顺序关系时,"小学"是"初中"的前件,而"初中"是小学的后件。
同样,"初中"是"高中"的前件,"高中"是"初中"的后件。
前后件关系是数据元素之间的一个基本关系,但前后件关系所表示的实际意义随具体对象的不同而不同。
一般来说,数据元素之间的任何关系都可以用前后件关系来描述。
数据结构的两个要素--"数据"和"结构"是紧密联系在一起的,"数据"是有结构的数据,而不是无关联的、松散的;而"结构"就是数据元素间的关系,是由数据的特性所决定的。
数据结构作为计算机的一门学科,主要研究和讨论以下三个方面:
(1)数据集合中个数据元素之间所固有的逻辑关系,即数据的逻辑结构;
(2)在对数据元素进行处理时,各数据元素在计算机中的存储关系,即数据的存储结构;
(3)对各种数据结构进行的运算。
讨论以上问题的目的是为了提高数据处理的效率,即提高数据处理的速度以及尽量节省在数据处理过程中所占用的计算机存储空间。
1.2.1.1数据的逻辑结构
由数据结构的定义可知,一个数据结构应包含以下两方面信息:
(1)表示数据元素的信息;
(2)表示各数据元素之间的前后件关系。
在此定义中,并没有考虑数据元素的存储,所以上述的数据结构实际上是数据的逻辑结构。
数据的逻辑结构是对数据元素之间的逻辑关系的描述,它可以用一个数据元素的集合和定义在此集合中的若干关系来表示。
数据的逻辑结构有两个要素:
一是数据元素的集合,通常记为D;二是D上的关系,它反映了数据元素之间的前后件关系,通常记为R。
一个数据结构可以表示成
B=(D,R)
其中B表示数据结构。
为了反映D中各数据元素之间的前后件关系,一般用二元组来表示。
例如,如果把一日三餐看作一个数据结构,则可表示成
B=(D,R)
D={早餐,午餐,晚餐}
R={(早餐,午餐),(午餐,晚餐)}
数据的逻辑结构包括线性结构、树型结构图、网状结构图和集合图4种。
1.2.1.2数据的存储结构
数据的逻辑结构在计算机存储空间中的存放形式称为数据的存储结构(也称数据的物理结构)。
在进行数据处理时,被处理的各数据元素总是被存放在计算机的存储空间中,而且各数据元素在计算机存储空间中的位置关系与它们的逻辑关系可能不同。
由于数据元素在计算机存储空间中的位置关系可能与逻辑关系不同,因此,为了表示存放在计算机存储空间中的各数据元素之间的逻辑关系(即前后件关系),在数据的存储结构中,不仅要存放各数据元素的信息,还需要存放各数据元素之间的前后件关系的信息。
一种数据的逻辑结构根据需要可以表示成多种存储结构,常用的存储结构有顺序、链接、索引等存储结构。
而采用不同的存储结构,其数据处理的效率是不同的。
因此,在进行数据处理时,选择合适的存储结构是很重要的。
1.2.3线性结构与非线性结构
如果在一个数据结构中一个数据元素都没有,则称该数据结构为空的数据结构。
在只有一个数据元素的数据结构中,删除该数据元素,就得到一个空的数据结构;在一个空的数据结构中插入一个新的元素后变成非空。
根据数据结构中各数据元素之间前后件关系的复杂程度,一般将数据结构分为两大类型:
线性结构与非线性结构。
如果一个非空的数据结构满足下列两个条件:
(1)有且只有一个根结点;
(2)每一个结点最多有一个前件,也最多有一个后件。
则称该数据结构为线性结构。
线性结构又称为线性表。
由此可见,在线性结构中,各数据元素之间的前后件关系是很简单的。
需要特别说明的是,在一个线性表中插入或删除任何一个结点后还应是线性结构。
如果一个数据结构不是线性结构,则称之为非线性结构。
在非线性结构中,各数据元素之间的前后件关系要比线性结构复杂。
链式结构是总常用的非线性结构。
线性结构与非线性结构都可以是空的数据结构。
对于空的数据结构,如果对该数据结构的运算是按线性结构的规则来处理的,则属于线性结构;否则属于非线性结构。
1.3线性表及顺序存储结构
1.4栈和队列
1.4.1栈及其基本运算
1.栈的定义
栈(Stack)是一种特殊的线性表,它所有的插入与删除都限定在表的同一端进行。
在栈中,一端是封闭的,既不允许进行插入元素,也不允许删除元素;另一端是开口的,允许插入和删除元素。
例如,枪械的子弹匣就可以用来形象地表示栈结构。
如图1-9(a)所示,子弹匣的一端是完全封闭的,最后被压入弹匣的子弹总是最先被弹出,而最先被压入的子弹最后才能被弹出。
在栈中,允许插入与删除的一端称为栈顶,不允许插入与删除的另一端称为栈底。
当栈中没有元素时,称为空栈。
例如,没有子弹的子弹匣为空栈。
通常用指针top来指示栈顶的位置,用指针bottom来指向栈底。
假设栈S=(a1,a2,…,an),则称a1为栈底元素,an为栈顶元素。
栈中元素按a1,a2,…,an的次序进栈,退栈的第一个元素应为栈顶元素an。
图1-9(b)是栈的入栈、退栈示意图。
2.栈的特点
根据栈的上述定义,栈具有以下特点。
栈的修改原则是"后进先出"(LastInFirstOut,LIFO)或"先进后出"(FirstInLastOut,FILO),因此,栈也称为"后进先出"表或"先进后出"表。
3.栈的基本运算
栈的基本运算包括入栈、退栈和读栈定元素。
(1)入栈运算
入栈运算是指在栈顶位置插入一个新元素。
首先将栈顶指针加1(即top加1),然后将新元素插入到栈顶指针指向的位置。
当栈顶指针已经指向存储空间的最后一个位置时,说明栈空间已满,不可能再进行入栈操作。
这种情况称为栈"上溢"错误。
(2)退栈运算
退栈是指取出栈顶元素并赋给一个指定的变量。
首先将栈顶元素(栈顶指针指向的元素)赋给一个指定的变量,然后将栈顶指针减1(即top减1)。
当栈顶指针为0时,说明栈空,不可进行退栈操作。
这种情况称为栈的"下溢"错误。
(3)读栈顶元素
读栈顶元素是指将栈顶元素赋给一个指定的变量。
这个运算不删除栈顶元素,只是将它赋给一个变量,因此栈顶指针不会改变。
当栈顶指针为0时,说明栈空,读不到栈顶元素。
图1-10所示是一个顺序表示的栈的动态示意图。
随着元素的插入和删除,栈顶指针top反应了栈的状态不断地变化。
1.4.2队列及其基本运算
1.4.2.1队列的定义及运算
队列也是一种特殊的线性表。
队列是指允许在一端进行插入,而在另一端进行删除的线性表。
允许进行删除运算的一端称为队头(或排头),允许进行插入运算的一端称为队尾。
若有队列:
Q=(q1,q2,…,qn)
那么,q1为队头元素(排头元素),qn为队尾元素。
队列中的元素是按照q1,q2,…,qn的顺序进入的,退出队列也只能按照这个次序依次退出,也就是说,只有在q1,q2,…,qn-1都退队之后,qn才能退出队列。
因最先进入队列的元素将最先出队,所以队列具有"先进先出"的特性,体现"先来先服务"的原则。
队头元素q1是最先被插入的元素,也是最先被删除的元素。
队尾元素qn是最后被插入的元素,也是最后被删除的元素。
因此,与栈相反,队列又称为"先进先出"(FirstInFirstOut,FIFO)或"后进后出"(LastInLastOut,LILO)的线性表。
例如,火车进隧道,最先进隧道的是火车头,最后进的是火车尾,而火车出隧道的时候也是火车头先出,火车尾后出。
可以用顺序存储的线性表来表示队列,为了指示当前执行退队运算的队头位置,需要一个队头指针(排头指针)front,为了指示当前执行入队运算的队尾位置,需要一个队尾指针rear。
排头指针front总是指向队头元素的前一个位置,而队尾指针rear总是指向队尾元素。
往队列的队尾插入一个元素称为入队运算,从队列的排头删除一个元素称为退队运算。
1.4.2.2循环队列及其运算
在实际应用中,队列的顺序存储结构一般采用循环队列的形式。
所谓循环队列,就是将队列存储空间的最后一个位置绕到第一个位置,形成逻辑上的环状空间。
在循环队列中,用队尾指针rear指向队列中的队尾元素,用排头指针front指向排头元素的前一个位置。
因此,从排头指针front指向的后一个位置直到队尾指针rear指向的位置之间所有的元素均为队列中的元素。
一维数组(1:
m),最大存储空间为m,数组(1:
m)作为循环队列的存储空间时,循环队列的初始状态为空,即front=rear=m,图1-13所示是循环队列初始状态的示意图。
循环队列的初始状态为空,即front=rear=m。
循环队列主要有两种基本运算:
入队运算和退队运算。
(1)入队运算
入队运算是指在循环队