NOIP高中信息技术竞赛资料-----数据结构

上传人:风*** 文档编号:68453864 上传时间:2022-04-02 格式:DOC 页数:69 大小:1.36MB
收藏 版权申诉 举报 下载
NOIP高中信息技术竞赛资料-----数据结构_第1页
第1页 / 共69页
NOIP高中信息技术竞赛资料-----数据结构_第2页
第2页 / 共69页
NOIP高中信息技术竞赛资料-----数据结构_第3页
第3页 / 共69页
资源描述:

《NOIP高中信息技术竞赛资料-----数据结构》由会员分享,可在线阅读,更多相关《NOIP高中信息技术竞赛资料-----数据结构(69页珍藏版)》请在装配图网上搜索。

1、精选优质文档-倾情为你奉上第1章 绪论程序设计就是使用计算机解决现实世界中的实际问题。对于给定的一个实际问题,在进行程序设计时,首先要把实际问题中用到的信息抽象为能够用计算机表示的数据;第二要把抽象出来的这些数据建立一个数据模型,这个数据模型也称为逻辑结构,即建立数据的逻辑结构;第三要把逻辑结构中的数据及数据之间的关系存放到计算机中,即建立数据的存储结构;最后在所建立的存储结构上实现对数据元素的各种操作,即算法的实现。本章就是要使大家了解计算机中的数据表示,理解数据元素、逻辑结构、存储结构和算法的有关概念;掌握基本逻辑结构和常用的存储方法,能够选择合适的数据的逻辑结构和存储结构;掌握算法及算法

2、的五个重要特性,能够对算法进行时间复杂度分析,从而选择一个好的算法,为后面的学习打下良好的基础。1.1基本概念和术语1.数据(data):是对客观事物的符号的表示,是所有能输入到计算机中并被计算机程序处理的符号的总称。2.数据元素(data element):是数据的基本单位,在计算机程序中通常作为一个整体来处理。一个数据元素由多个数据项(data item)组成,数据项是数据不可分割的最小单位。3.数据结构(data structure):是相互之间存在一种或多种特定关系的数据元素的集合。数据结构是一个二元组,记为:data_structure=(D,S).其中D为数据元素的集合,S是D上关

3、系的集合。数据元素相互之间的关系称为结构(structure)。根据数据元素之间关系的不同特性,通常由下列四类基本结构:(1)集合:数据元素间的关系是同属一个集合。(图1)(2)线性结构:数据元素间存在一对一的关系。(图2)(3)树形结构:结构中的元素间的关系是一对多的关系。(图3)(4)图(网)状结构:结构中的元素间的关系是多对多的关系。(图4) 图1 图2 图3 图41.2 数据的逻辑结构和物理结构1.逻辑结构:数据元素之间存在的关系(逻辑关系)叫数据的逻辑结构。即上面给出的四种结构。2.物理结构:数据结构在计算机中的表示(映象)叫数据的物理结构,又称存储结构。一种逻辑结构可映象成不同的存

4、储结构:顺序存储结构和非顺序存储结构(链式存储结构和散列结构)。1.3算法及算法分析1. 算法:是对特定问题求解步骤的一种描述,是指令的有限序列。一个算法是一系列将输入转换为输出的计算步骤。 2. 算法的重要特性 输入:一个算法应该有零个或多个输入。 有穷性:一个算法必须在执行有穷步骤后正常结束,而不能形成无穷循环。 确定性:算法中的每一条指令必须有确切的含义,不能产生多义性。 可行性:算法中的每一条指令必须是切实可执行的,即原则上可以通过已经实现的基本运算执行有限次来实现。 输出:一个算法应该有一个或多个输出,这些输出是同输入有某个特定关系的量。 3. 算法的时间复杂度定义:设问题的规模为n

5、,把一个算法的时间耗费T(n)称为该算法的时间复杂度,它是问题规模为n的函数。算法的渐进时间复杂度设T(n)为一个算法的时间复杂度,如果当n趋向无穷大时T(n)与函数f(n)的比值的极限是一个非零常数M,即记作T(n)=O(f(n),则称O(f(n)为算法的渐进时间复杂度,简称时间复杂度,也称T(n)与f(n)的数量级相同,通常,f(n)应该是算法中频度最大的语句的频度。常用的算法的时间复杂度的顺序O(1)O(lgn)O(n)O(nlgn)O(n2)O(n3)=0&(Ai!=k)(3)i-;(4)return i;此算法中的语句(3)的频度不仅与问题规模n有关,还与输入实例中A的各元素取值及K

6、的取值有关:若A中没有与K相等的元素,则语句(3)的频度f(n)=n;若A的最后一个元素等于K,则语句(3)的频度f(n)是常数0。 最坏时间复杂度和平均时间复杂度最坏情况下的时间复杂度称为最坏时间复杂度。一般不特别说明,讨论的时间复杂度均是最坏情况下的时间复杂度。这样做的原因是:最坏情况下的时间复杂度是算法在任何输入实例上运行时间的上界,这就保证了算法的运行时间不会比任何情况下更长。平均时间复杂度是指所有可能的输入实例均以等概率出现的情况下,算法的期望运行时间。 例1 求下列交换两个变量i和j的值的算法的时间复杂度。(1) i=10;(2) j=20;(3) t=i;(4) i=j;(5)

7、j=t;解:各行语句的执行次数均为1,所以该算法的时间耗费T(n)= 1+1+1+1+1=5,该算法的时间耗费T(n)与问题的规模n无关,因此,该算法的时间复杂度T(n)=O(1)。例2 求两个n阶方阵的乘积C=AB,其算法如下,计算该时间复杂度。程序段:(1) for(i=0; in; i+)(2) for(j=0; jn; j+)(3) cij=0;(4) for(k=0; kn; k+)(5) cij+=aik*bkj; 解:解法1计算程序段中的每一行的执行次数。第(1)行for(i=0; in; i+)中只考虑循环条件表达式in的执行次数(忽略初始化表达式i=0和修正表达式i+的执行次

8、数,下同),表达式in共执行n+1次(i为0到n-1时该表达式非零,共n次,i为n时该表达式为零,共1次,合计执行n+1次),所以,第(1)行共执行n+1次;第(2)行for(j=0; jn; j+),在第(1)行for(i=0; in; i+)中的表达式in非零时(共n次)都要执行一遍,而每一遍同样要执行n+1次,所以,第(2)行共执行n(n+1)次;第(3)行cij=0;在表达式in和jn均非零时执行,共执行n2次;第(4)行for(k=0; kn; k+)在表达式in和jn均非零时执行一遍,而每一遍同样要执行n+1次,所以,第(4)行共执行n2(n+1)次;第(5)行cij+=aik*b

9、kj; 在表达式in、jn和kn均非零时执行,共执行n3次;因此,各行执行次数分别为:n+1,n(n+1),n2,n2(n+1),n3。如果用T(n)表示该算法的时间耗费,则 T(n)= n+1+n(n+1)+n2+n2(n+1)+n3=2n3+3n2+2n+1由此可知,该算法的时间耗费T(n)是矩阵阶数n的函数,T(n)=O(n3)。解法2只计算执行频度最高的行。显然,在该程序段中,执行频度最高的行为cij+=aik*bkj; 在表达式in、jn和kn均非零时执行,而表达式in、jn和kn均有n次非零,所以,该行共执行n3次。因此,该算法的时间耗费T(n)是矩阵阶数n的函数,T(n)=O(n

10、3)。【课堂实践】分析并计算下面程序段执行的时间复杂度。i=1; k=0;while(i0,则除a1和an外,有且仅有一个直接前驱和一个直接后继,即ai(其中1 in)是线性表中第i个数据元素,在ai之前仅有一个数据元素ai-1,而在ai之后也仅有一个数据元素ai+1。称a1称为起始结点,an称为终端结点,i称为ai在线性表中的序号或位置。线性表中结点的之间的关系就是上述的邻接关系,由于该关系是线性的,我们称线性表是一种线性结构。2.线性表的基本运算(1)线性表初始化格式:InitList(L)初始条件:线性表L不存在。操作结果:构造一个空的线性表L。(2)求线性表的长度格式:LengthLi

11、st(L)初始条件:线性表L存在。操作结果:返回线性表L中所有元素的个数。(3)取表元格式:GetList(L,i)初始条件:线性表L存在,且1iLengthList(L)。操作结果:返回线性表L的第i个元素(ai)的值。(4)按值查找格式:LocateList(L,x)初始条件:线性表L存在,x有确定的值。操作结果:在线性表L中查找值为x的数据元素,并返回该元素在L中的位置。若L中有多个元素的值与x相同,则返回首次找到的元素的位置;若L中没有值为x的数据元素,返回一个特殊值(通常为-1)表示查找失败。(5)插入操作格式:InsertList(L,i,x)初始条件:线性表L存在,i为插入位置(

12、1in+1,n为插入前的表长)。操作结果:在线性表L的第i个元素(ai)位置上插入值为x的新元素,原序号为i,i+1, ,n的数据元素的序号插入后变为i+1,i+2, ,n+1,插入后表长=原表长+1。(6)删除操作格式:DeleteList(L,i)初始条件:线性表L存在,i(1in)为给定的待删除元素的位置值。操作结果:在线性表L中删除序号为i的数据元素(ai),删除后,原序号为i+1,i+2, ,n的数据元素的序号变为i,i+1, ,n-1,删除后表长=原表长-1。注:以上给出的是线性表的基本运算,并不是全部运算,其它运算可用这些基本运算来实现,这些运算都是定义在逻辑结构层次上的运算,只

13、有确定了存储结构之后,才能具体实现这些运算。例1 假设两个线性表LA,LB分别代表两个集合A和B:求A=A U Bvoid union(List &La ,List &Lb)/将所有在线性表Lb中,但不在La中的数插入到La中La.len=ListLength(La);Lb.len=ListLength(Lb); /求两表的长度for(i=1;i=Lb.len;i+) GetElem(Lb,i,e);/取Lb中第i个的元素复制给eIf(!LocateElem(La,e,equal)ListInsert(La,+La.len,e);/若其中不存在相同的,则插入/union例2 已知线性表la,l

14、b中的数据元素按值非递减有序排列,现要求将la,lb归并为一个新的线性表lc且lc按值非递减。例如 la=(3,5,8,11), Lb=(2,6,8,9,11,15,20)则lc=(2,3,5,6,8,8,9,11,11,15,20)分析:由于两表都是按照一定顺序进行排列,所有设置2个指针,分别指向a、b表,先分别比较比较最初指向的两数据,比较一下大小,谁小就放入到c表中,然后数小的指针向下移动,再进行比较。直到2表有一表结束,再将剩余的表存放到c中。Void MergeList(List La, List Lb,List &Lc)/已知线性表la和lb中的数据元素InitList(Lc);/

15、初始化表cInt i=j=1;k=0;La.len=ListLength(La);Lb.len= ListLength(Lb);While(i= La.len)&( (j= Lb.len)GetElem(La,i,ai);GetElem(Lb,j,bj);/获取数值If(ai=bj)ListInsert(Lc,+k,ai);i+;elseListInsert(Lc,+k,bj);j+;/if结束/whie结束,其中一表结束;While(i=La.len)/表B数据全访问完,表a未到最后GetElem(La,i+,ai);ListInsert(Lc,+k,ai);While(j=Lb.len)/

16、表a数据全访问完,表b未到最后GetElem(Lb,j+,bj);ListInsert(Lc,+k,bj);/程序结束分析:上述2个算法的时间复杂度(基本操作的执行时间),例1为O(ListLength(La)*ListLength(Lb),例2的时间复杂度为O(ListLength(La)+ListLength(Lb)。2.2 线性表的顺序存储结构线性表的顺序存储即用一组地址连续的存储单元依次存储线性表中的元素。设线性表中每个元素需占用r个存储单元则 loc(ai+1 )=loc(ai)+r loc(ai)=loc(a1)+(i-1)*r线性表的这种机内表示称做线性表的顺序存储结构或顺序映像

17、,通常,称这种存储结构的线性表为顺序表。只要确定了存储线性表的起始位置,线性表中任一数据元素可随机存储,所以线性表的顺序结构是一种随机存储的存储结构。/=线性表的动态顺序存储结构#define LIST_INIT_SIZE 100 / 初始分配量#define LISTINCREMENT 10 /分配增量Typedef structElemtype *elem; /存储空间基址Int length; /当前长度Int listsize; /当前分配的存储容量SqList;Elem表示基地址,length指示线性表的当前长度。上述的含义是为顺序表分配一个预定义大小的数据空间,并将当前的长度设为0

18、;Status InitList_sq(SqList &L)/=创建一个空的线性表L.elem=(ElemType*)malloc(LIST_INIT_SIZE*sizeof(ElemType);/ ElemType指数据类型,malloc新分配空间If(!L.elem) exit(OVERFLOW);/存储分配失败L.length=0;/空表长度为0L.listsize= LIST_INIT_SIZE;/初始存储容量Return ok;/InitList_sq在这种存储方式下,容易实现对表的遍历。要在表的尾部插入一个新元素,也很容易。但是要在表的中间位置插入一个新元素,就必须先将其后面的所有

19、元素都后移一个单元,才能腾出新元素所需的位置。Status ListInsert_sq(SqList &L,int I,ElemType e)/在顺序表中插入一个新元素 eIf(iL.length+1) return Error;If(L.length= L.listsize)/当前存储空间已满,增加分配Newbase=(ElemType*)realloc(L.elem,(LIST_INIT_SIZE+LISTINCREMENT)*sizeof(ElemType);/ ElemType指数据类型,realloc再次分配,L.elem存储基地址/ifq=&(L.elemi-1);/q为插入位置f

20、or(p=&(L.elemL.length-1);p=q;-p)*(p+1)=*q;/for*q=e;/插入e+ L.length;Return ok;执行删除运算的情形类似。如果被删除的元素不是表中最后一个元素,则必须将它后面的所有元素前移一个位置,以填补由于删除所造成的空缺。这两种运算的时间复杂度均为O(n)n为表的长度。Status ListDelete_sq(SqList &L,int I,ElemType &e)/在顺序表中插入一个新元素 eIf(iL.length+1) return Error;p=&(L.elemi-1);/p为删除位置e=*p;q=L.elem+L.lengt

21、h-1;for(+p;pnext=p-next; p-next=s;上述算法中,链表指针的修改情况见图2图2(a)是执行Insert运算之前的情况。我们要在指针p所指的单元之后插入一个新元素x。图2(b)是执行Insert运算以后的结果,其中的虚线表示新的指针。在上述Insert算法中,位置变量p指向单链表中一个合法位置,要插入的新元素x应紧接在p所指单元的后面。指针p的合法性应在执行Insert运算之前判定。往一个单链表中插入新元素通常在表头或表尾进行,因此p的合法性容易判定。Insert运算所需的时间显然为O(1)。(2)Delete(p)功能:从表L中删除位置p的下一元素。例如,当L为a

22、1,a2,an时,执行Delete(p)后,L变为a1,a2,ap-1,ap+1,an 。当L中没有位置p或p=End(L)时,该运算无定义。实现:p.next=p.next.next;这个过程很简单,其指针的修改如图3所示。若要从一个表中删除一个元素x,但不知道它在表中的位置,则应先用Locate(x,L)找出指示要删除的元素的位置,然后再用Delete删除该位置指示的元素。Delete过程所需的时间显然也为O(1)。2.静态链表静态链表:用游标指示数组单元地址的下标值,它属整数类型数组元素是记录类型,记录中包含一个表元素和一个作为游标的整数;具体说明如下:type jid=record d

23、ata:datatype; next:integer; end;var alist=array0.maxsize of jid游标即我们可以用游标来模拟指针, 对于一个表L,我们用一个整型变量Lhead(如Lhead=0)作为L的表头游标。这样,我们就可以用游标来模拟指针,实现单链表中的各种运算。当i1时,表示第i个位置的位置变量pi的值是数组alist中存储表L的第i-1个元素next值,用p:=alist(p).next后移。照此,我们虽然是用数组来存储表中的元素,但在作表的插人和删除运算时,不需要移动元素,只要修改游标,从而保持了用指针实现表的优点。因此,有时也将这种用游标实现的表称为静

24、态链表。3.循环链表循环链表是另一种链式存储结构,特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环。因此,可由表中任一结点出发均可找到表中其他结点。如图6所示的是一个单链的循环链表或简称为单循环链表。(a)非空表(b)空表图6单循环链表在单循环链表上实现表的各种运算的算法与单链表的情形是类似的。它们仅在循环终止条件上有所不同:前者是p或p.next指向表头单元;后者是p或p.next指向空(nil)。在单链表中我们用指向表头单元的指针表示一个表L,这样就可以在O(1)时间内找到表L中的第一个元素。然而要找到表L中最后一个元素就要花O(n)时间遍历整个链表。在单循环链表中,我们也可以

25、用指向表头单元的指针表示一个表L。但是,如果我们用指向表尾的指针表示一个表L时,我们就可以在O(1)时间内找到表上中最后一个元素。同时通过表尾单元中指向表头单元的指针,我们也可以在O(1)时间内找到表L中的第一个元素。在许多情况下,用这种表示方法可以简化一些关于表的运算。4.双链表单循环链表中,只有一个指示直接后继的指针域,虽然从任一单元出发,可以找到其前驱单元,但需要O(n)时间。如果我们希望能快速确定表中任一元素的前驱和后继元素所在的单元,可以在链表的每个单元中设置两个指针,一个指向后继,另一个指向前驱,形成图8所示的双向链表或简称为双链表。图8 双链表由于在双链表中可以直接确定一个单元的

26、前驱单元和后继单元,我们可以用一种更自然的方式表示元素的位置,即用指向双链表中第i个单元而不是指向其前一个单元的指针来表示表的第i个位置。双链表的单元类型定义如下。 和单链的循环表类似,双链表也可以有相应的循环表。我们用一个表头单元将双链表首尾相接,即将表头单元中的previous指针指向表尾,并将表尾单元的next指针指向表头单元,构成如图9所示的双向循环链表。图9 双向循环链表下面仅以双向循环链表为例给出三种基本运算的实现。(1)在双向循环链表L的位置p处插入一个新元素x的过程Insert可实现如下。 上述算法对链表指针的修改情况见图10。图10 在双向循环链表中插入一个元素算法Inser

27、t中没有检查位置p的合法性,因此在调用Insert之前应保证位置p的合法性。由于插入通常是在表的头尾两端进行的,所以容易检查位置p的合法性。(2)从双向循环链表L中删除位置p处的元素可实现如下:上述算法对链表指针的修改情况见图11。图11 从双向循环链表中删除一个元素5.链表的应用例1:求 (A-B)U(B-A)其中A,B代表两个集合(用静态链表)例2 求p1(x)+p2(x) (两个多项式的和)练习题:1.约瑟夫问题:有M个猴子围成一圈,每个有一个编号,编号从1到M。打算从中选出一个大王。经过协商,决定选大王的规则如下:从第一个开始,每隔N个,数到的猴子出圈,最后剩下来的就是大王。要求:从键

28、盘输入M,N,编程计算哪一个编号的猴子成为大王。()2.求多项式的减与乘法.()第3章 栈栈是一种特殊的线性表,它的逻辑结构与线性表相同,只是其运算规则较线性表有更多的限制,故又称它为运算受限的线性表。3.1 栈的概念及运算栈(Stack)是限制仅在表的一端进行插入和删除运算的线性表。向栈中插入元素称为进(入)栈,从栈中删除元素称为退(出)栈。通常插入、删除的这一端称为栈顶,由于元素的进栈和退栈,使得栈顶的位置经常是变动的,因此需要用一个整型量Top指示栈顶的位置,通常称Top 为栈顶指针;另一端称为栈底。当表中没有元素时称为空栈,即Top=-1。栈的修改是按后进先出的原则进行。每次删除的总是

29、当前栈中“最新”的元素,即最后插入的元素,而最先插入的是被放在栈的底部,要到最后才能删除。假设一个栈S中的元素为an,an-1,.,a1,则称a1为栈底元素,an为栈顶元素。栈中的元素按a1 ,a2,.,an-1,an的次序进栈。在任何时候,出栈的元素都是栈顶元素。换句话说,栈的修改是按后进先出的原则进行的,如图1所示。因此,栈又称为后进先出(Last In First Out)表,简称为LIFO表。所以,只要问题满足LIFO原则,就可以使用栈。 图1栈的运算:为一种抽象数据类型,常用的栈运算有:运算含义inistack(S)使S成为一个空栈。getTop(S)这是一个函数,函数值为S中的栈顶

30、元素。Pop(S)从栈S中删除栈顶元素,简称为抛栈。Push(S,x)在S的栈顶插入元素x,简称为将元素x入栈。Empty(S)这是一个函数。当S为空栈时,函数值为true,否则函数值为false。3.2 栈的存储与实现1.顺序栈及基本操作的实现由于栈是运算受限线性表,因此线性表的存储结构对栈也适用,而线性表有顺序存储和链式存储两种,所以,栈也有顺序存储和链式存储两种。(1)顺序栈:栈的顺序存储结构简称为顺序栈,它是运算受限的顺序表。(2)顺序栈的描述#define StackSize 100 /假定栈空间最多为100个元素typedef char DataType;/假定栈元素的类型为字符类

31、型typedef structDataType dataStackSize;/栈元素定义int top;/栈指针定义SeqStack; SeqStack *S;/ 定义栈S设S是SeqStack类型的指针变量,则栈顶指针可表示为S- top;若栈底位置在向量的低端,则S-data0是栈底元素,栈顶元素可表示为S-dataS- top。注意:有元素x进栈时,需要先将S-top加1,然后再将元素进栈,即依次完成下列操作:+S-top;S-dataS- top=x;。当栈顶元素做退栈操作后,需要将S-top减1,即完成操作:S-top-;。条件S-top=StackSize-1表示栈满;S-top=

32、-1表示栈空。当栈满时,再做进栈运算所产生的空间溢出现象称为上溢。上溢是一种出错状态,应设法避免。当栈空时,做退栈运算所产生的溢出现象称为下溢。3、顺序栈上基本运算的算法置栈空void InitStack(SeqStack *S)/将顺序栈置空 S-top=-1;判栈空如果栈S为空,则S-top=-1,此时应该返回1,而关系表达式S-top=-1的值为1;如果栈S为非空,则S-top!=-1,此时应该返回0,而关系表达式S-top=-1的值为0,因此,无论怎样只需返回S-top=-1的值。int StackEmpty(SeqStack *S)return S-top=-1;判栈满与判栈空的道理

33、相同,只需返回S-top=StackSize-1。int StackFull(SeqStack *S)return S-top=StackSize-1;进栈进栈操作需要将栈和进栈元素的值作为函数参数,由于使用栈指针作为函数参数,对栈进行操作,所以进栈函数不需要有返回值;进栈操作时,需要判断是否栈满,当栈不满时,先将栈顶指针加1,再进栈。int Push(SeqStack *S,DataType x)if (StackFull(S)puts(栈满); return 0; S-data+S-top=x;/栈顶指针加1后将x入栈return 1;退栈退栈操作需要将栈指针作为函数参数,并返回栈顶元素的

34、值,所以函数返回值的类型为DataType;退栈操作时,需要判断是否栈空,当栈不空时,先退栈,再将栈顶指针减1,可以先将栈顶元素的值记录下来,然后栈顶指针减1,最后返回记录下来的值,也可以像给出的退栈函数那样来操作。DataType Pop(SeqStack * S)if(StackEmpty(S)puts(栈空); return 0;return S-dataS-top-;/返回栈顶元素并将栈顶指针减1取栈顶元素取栈顶元素与退栈的区别在于,退栈需要改变栈的状态,而取栈顶元素不需要改变栈的状态。DataType StackTop(SeqStack *S)if(StackEmpty(S)puts

35、(栈空); return 0;return S-dataS-top;由于栈的插入和删除操作具有它的特殊性,所以用顺序存储结构表示的栈并不存在插入删除数据元素时需要移动的问题,但栈容量难以扩充的弱点仍就没有摆脱。例 元素a1,a2,a3,a4依次进入顺序栈,则下列不可能的出栈序列是Aa4, a3, a2, a1 Ba3, a2, a4, a1Ca3, a4, a2, a1 Da3, a1, a4, a2分析:对于A,由于元素a1,a2,a3,a4依次进栈,而a4先出栈,说明a1,a2,a3已经入栈,所以出栈顺序只能是a4,a3,a2,a1,因此A是正确的出栈序列;对于B,C,D,由于都是a3先出

36、栈,说明a1,a2已经入栈,所以a1,a2的相对位置一定是不变的,这就是a2一定在a1之前出栈,比较上述三个答案,只有D中的a1出现在a2的前面,这显然是错误的。因此,答案为D。2.链栈及基本操作的实现若栈中元素的数目变化范围较大或不清楚栈元素的数目,就应该考虑使用链式存储结构。用链式存储结构表示的栈称作“链栈”。链栈通常用一个无头结点的单链表表示。由于栈的插入删除操作只能在一端进行,而对于单链表来说,在首端插入删除结点要比尾端相对地容易一些,所以,我们将单链表的首端作为栈顶端,即将单链表的头指针作为栈顶指针。链栈如图3.2。图3.2 链栈top栈顶栈底(1)链栈:栈的链式存储结构称为链栈,它

37、是运算受限的单链表,其插入和删除操作仅限制在表头位置上进行,栈顶指针就是链表的头指针。(2)链栈的描述typedef char DataType;/假定栈元素的类型为字符类型typedef struct stacknode/结点的描述DataType data;struct stacknode *next;StackNode;typedef struct/栈的描述StackNode*top; /栈顶指针LinkStack; LinkStack *S; /定义指向链栈的指针S设S是LinkStack类型的指针变量,则S是指向链栈的指针,链栈栈顶指针可表示为S- top;链栈栈顶元素可表示为S-

38、top -data。设p是StackNode类型的指针变量,则p是指向链栈某结点的指针,该结点的数据域可表示为p -data,该结点的指针域可表示为p - next。注意:LinkStack结构类型的定义是为了方便在函数体中修改top指针本身。若要记录栈中元素个数,可将元素个数属性作为LinkStack类型中的成员定义。 条件S-top= NULL表示空栈,链栈没有栈满的情况。3、链栈上基本运算的算法置栈空 void InitStack(LinkStack *S)/将链栈置空S-top=NULL;判栈空int StackEmpty(LinkStack *S) return S-top=NULL

39、;进栈void Push(LinkStack *S,DataType x )/将元素x插入链栈头部StackNode *p=(StackNode *)malloc(sizeof(StackNode);p-data=x;p-next=S-top;/将新结点*p插入链栈头部S-top=p; /栈顶指针指向新结点退栈DataType Pop(LinkStack *S)DataType x;StackNode *p=S-top;/保存栈顶指针if(StackEmpty(S)puts(“栈空”); return 0;/下溢,退出运行x=p-data; /保存栈顶结点数据S-top=p-next; /将栈

40、顶结点从链上摘下free(p);return x; 取栈顶元素DataType StackTop(LinkStack *S)if(StackEmpty(S)puts(“栈空”); return 0; return S-top-data; 注:由于链栈中的结点是动态分配的,可以不考虑上溢,所以无须定义StackFull运算。3.3 栈的应用1.数值转换十进制整数N向其它进制数d(二、八、十六)的转换是计算机实现计算的基本问题。转换法则:该转换法则对应于一个简单算法原理:n=(n div d)*d+n mod d其中:div为整除运算,mod为求余运算例如 (1348)10= (2504)8,其运

41、算过程如下: n n div 8 n mod 8 1348 168 4 168 21 0 21 2 5 2 0 2采用静态顺序栈方式实现 void conversion(int n , int d) /*将十进制整数N转换为d(2或8)进制数*/ SqStack S ; int k, *e ;S=Init_Stack();while (n0) k=n%d ; push(S , k) ; n=n/d ; /* 求出所有的余数,进栈 */while (S.top!=0) /* 栈不空时出栈,输出 */ pop(S, e) ; printf(“%1d” , *e) ; 2.表达式的求值问题:能否设计

42、算法,编制一个程序,让计算机扫描如下表达式,并将其值打印出来。# 3 * ( 4 + 8 ) / 2 -5 #注:给表达式设置#,标志扫描的开始和结束。这个表达式的计算顺序是:3*(4+8)/2-5=3*12/2-5=36/2-5=18-5=13任何一个表达式都是由操作数、运算符和界位符组成的,操作数可以使一些常数也可以使一些变量或是常量的标示符,运算符则分为算数运算符、关系运算符和逻辑运算符;基本界位符有左右括号和表达式结束。一般将运算符和界位符看做是界符。提示算法:为实现算符优先算法,可以实现2个工作栈,一个是操作数栈opnd,用来存放操作数,如3、4、8等,另一个是运算符栈optr,用来

43、存放运算符。首先将标志“#”进运算符栈的栈底。然后依次扫描,按照栈的后进先出原则进行:(1)遇到操作数,进操作数栈;(2)遇到运算符时,则需将此运算符的优先级与栈顶运算符的优先级比较,若若高于栈顶元素则进栈,继续扫描下一符号,否则,将运算符栈的栈顶元素退栈,形成一个操作码Q,同时操作数栈的栈顶元素两次退栈,形成两个操作数a、b,让计算机对操作数与操作码完成一次运算操作,即aQb,并将其运算结果存放在操作数栈中模拟计算机处理算术表达式过程。从键盘上输入算术表达式串(只含、运算符,充许含括号),输出算术表达式的值。设输入的表达式串是合法的。附源程序:3.背包问题问题:假设有n件质量分配为w1,w2

44、,.,wn的物品和一个最多能装载总质量为T的背包,能否从这n件物品中选择若干件物品装入背包,使得被选物品的总质量恰好等于背包所能装载的最大质量,即wi1+wi2+.+wik=T。若能,则背包问题有解,否则无解。算法思想:首先将n件物品排成一列,依次选取;若装入某件物品后,背包内物品的总质量不超过背包最大装载质量时,则装入(进栈);否则放弃这件物品的选择,选择下一件物品试探,直至装入的物品总和正好是背包的最大转载质量为止。这时我们称背包装满。若装入若干物品的背包没有满,而且又无其他物品可以选入背包,说明已装入背包的物品中有不合格者,需从背包中取出最后装入的物品(退栈),然后在未装入的物品中挑选,

45、重复此过程,直至装满背包(有解),或无物品可选(无解)为止。具体实现:设用数组weight1.N,stack1,N分别存放物品重量和已经装入背包(栈)的物品序号,MaxW表示背包的最大装载量。每进栈一个物品,就从MaxW中减去该物品的质量,设i为待选物品序号,若MaxW-weighti=0,则该物品可选;若MaxW-weighti n,则需退栈,若此时栈空,则说明无解。练习:完整完成背包问题第4章 队列队列与栈一样都是运算受限的线性表,但与栈的限制不同。4.1 队列的概念及运算队列是一种特殊的线性表,对这种线性表,删除操作只在表头(称为队头)进行,插入操作只在表尾(称为队尾)进行。队列的修改是

46、按先进先出的原则进行的,所以队列又称为先进先出(First In First Out)表,简称FIFO表。假设队列为a1,a2,.,an,那么a1就是队头元素,an为队尾元素。队列中的元素是按a1,a2,.,an的顺序进入的,退出队列也只能按照这个次序依次退出。也就是说,只有在a1离开队列之后,a2才能退出队列,只有在a1,a2,.,an-1都离开队列之后,an才能退出队列。图1是队列的示意图。图1队列的运算:为一种抽象数据类型,常用的队列运算有:4.2 队列的存储与实现队列是一种特殊的表,因此凡是可以用来实现表的数据结构都可以用来实现队列。不过,队列的中的元素的个数通常不固定,因此常用循环数

47、组和链表两种方法实现队列。1.顺序队列及基本操作(1)顺序队列:队列的顺序存储结构称为顺序队列,它是运算受限的顺序表。(2)顺序队列的表示 和顺序表一样,顺序队列用一个数组来存放当前队列中的元素。由于随着入队和出队操作的变化,队列的队头和队尾的位置是变动的,所以应设置两个整型量front和rear分别指示队头和队尾在向量空间中的位置,它们的初值在队列初始化时均应置为0。通常称front为队头指针,称rear为队尾指针。(3)顺序队列的基本操作入队:入队时将新元素插入rear所指的位置,然后将rear加1。出队:出队时删去front所指的元素,然后将front加1并返回被删元素。注意:当头尾指针

48、相等时,队列为空。在非空队列里,队头指针始终指向队头元素,尾指针始终指向队尾元素的下一位置。顺序队列基本操作如图3.3。frontrear0123(a)空队列ABCfrontrear0123(b)ABC入队frontrear0123top(d)BC出队BCfrontrear012(c)A出队3图3.3 顺序队列基本操作(4)顺序队列中的溢出现象“下溢”现象:当队列为空时,做出队操作产生的溢出现象。“真上溢”现象:当队列满时,做入队操作产生空间溢出的现象。“真上溢”是一种出错状态,应设法避免。“假上溢”现象:由于入队和出队操作中,头尾指针只增加不减少,致使被删元素的空间永远无法重新利用。当队列中

49、实际元素个数远远小于向量空间的规模时,也可能由于尾指针已超越向量空间的上界而不能做入队操作。该现象称为“假上溢”现象。(5)解决“假上溢”现象的方法当出现“假上溢”现象时,把所有的元素向低位移动,使得空位从低位区移向高位区,显然这种方法很浪费时间。把队列的向量空间的元素位置0Queuesize-1看成一个首尾相接的环形,当进队的队尾指针等于最大容量,即rear=Queuesize时,使rear=0。2.循环队列(1)循环队列:把向量空间的元素位置首尾相接的顺序队列称为循环队列。例如,设队列的容量Queuesize=8,元素a1,a2,a3,a4,a5,a6,a7依次入队,然后a1,a2,a3出

50、队的循环队列如图3.4。fronta4a7a6a576543210rear图3.4 循环队列(2)循环队列头尾指针的操作 循环队列Q进行出队、入队操作时,头尾指针仍要加1。当头尾指针指向向量上界(QueueSize-1)时,其加1操作的结果是指向向量的下界0。这种循环意义下的加1操作可以描述为: 利用选择结构if(i+1=QueueSize)/i为Q-front或Q-reari=0;else i+; 利用模运算i=(i+1)%QueueSize;/i为Q-front或Q-rear我们将采用此方法实现循环意义下的队头、队尾指针的加1操作。(3)循环队列边界条件的处理方法循环队列Q中,由于入队时尾

51、指针向前追赶头指针;出队时头指针向前追赶尾指针,造成队空和队满时头尾指针均相等。因此,无法通过条件Q-front= Q-rear来判别队列是“空”还是“满”。解决这个问题的方法至少有三种:另设一标志变量flag以区别队列的空和满,比如当条件Q-front= Q-rear成立,且 flag 为0时表示队列空,而为1时表示队列满。少用一个元素的空间。约定入队前,测试尾指针在循环意义下加1后是否等于队头指针,若相等则认为队满(注意:rear所指的单元始终为空),此时队空的条件是Q-front= Q-rear,队满的条件是(Q-rear+1)%QueueSize= Q-front。使用一个计数器cou

52、nt记录队列中元素的总数,当Q-count =0时表示队列空;当Q-count =QueueSize时表示队列满。 我们将使用此方法。(4)循环队列的描述#define QueueSize 100 /定义队列最大容量typedef char DataType; /定义队列元素类型typedef struct cirqueueDataType dataQueueSize;/队列元素定义int front;/队头指针定义int rear;/队尾指针定义int count;/计数器定义CirQueue; CirQueue *Q; /定义循环队列Q设Q是CirQueue类型的指针变量,则Q是指向循环队列的指针,队头指针、队尾指可分别表示为Q-front、Q- rear,计数器可表示为Q- count,队头元素可表示为Q-dataQ-front,队尾元素可表示为Q-dataQ- rear。(5)循环队列的基本运算的算法 置队空void InitQueue(CirQ

展开阅读全文
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!