1 / 76

第三章 栈和队列

第三章 栈和队列. 3.1 栈的表示和实现 3.2 栈的应用 -- 表达式求值 3.3 递归过程 3.4 队列的表示和实现 栈和队列的逻辑结构、物理结构,以及它们之间的相互关系; 定义与之相适应的运算; 设计相应的算法; 分析算法的效率。. 3.1 栈的表示和实现. 一、栈的概念 栈 ( stack )是 插入 和 删除 操作限定在 表尾 进行的 线性表 。 栈的逻辑表示为: S = ( a 1 ,a 2 , … ,a n ) 表尾元素 a n 称为 栈顶 (top) 表头元素 a 1 称为 栈底 (bottom)

aelwen
Download Presentation

第三章 栈和队列

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第三章 栈和队列 3.1 栈的表示和实现 3.2 栈的应用--表达式求值 3.3 递归过程 3.4 队列的表示和实现 • 栈和队列的逻辑结构、物理结构,以及它们之间的相互关系; • 定义与之相适应的运算; • 设计相应的算法; • 分析算法的效率。

  2. 3.1 栈的表示和实现 一、栈的概念 栈(stack)是插入和删除操作限定在表尾进行的线性表。 • 栈的逻辑表示为:S =(a1,a2, …,an) 表尾元素an称为栈顶(top) 表头元素a1称为栈底(bottom) • 不含元素的空表称为空栈 • 栈的运算特性是后进先出(Last In First Out--LIFO) 或先进后出(First In Last Out--FILO)

  3. 3.1 栈的表示和实现 二、栈的基本运算 • INISTACK(S)初始化操作,设定一个空栈S • EMPTY(S)判栈S是否为空函数(true/false) • PUSH(S, x)入栈操作,在栈S顶部插入元素x, 相当于线性表的INSERT(L, n+1, x) • POP(S)出栈函数,若S不空,则返回栈顶 元素,并删除栈顶元素;否则返回 空元素NULL,相当于线性表的DELET(L, n) • GETTOP(S)取栈顶元素函数,与POP(S)的差别在不删除栈 顶元素, 相当于线性表的GET(L, n)

  4. 3.1 栈的表示和实现 二、栈的基本运算 • CURRENT-SIZE(S)求S栈中当前元素个数函数, 相当于线性表的LENGTH(L) • CLEAR(S)置栈空操作

  5. 3.1 栈的表示和实现 三. 栈的表示和实现 1. 顺序栈的存储结构描述及出入栈运算 CONST arrmax = 栈允许存放元素个数的最大值; TYPE sqstktp = RECORD elem:ARRAY[1..arrmax] OF elemtp; top:0..arrmax END; S = sqstktp; • S.elem[i]表示第i个进栈的元素 • S.elem[S.top]表示栈顶元素 • 当S.top = 0 表示空栈 • 当S.top = arrmax 表示栈满

  6. 3.1 栈的表示和实现 入栈函数push(S,x)的实现 算法思想:若栈满返回false;否则将x入栈,并返回true。 FUNC push_stack (VAR s:sqstktp;x:elemtp):boolean; IF s.top = armax THEN RETURN(false) ELSE [s.top:=s.top+1; s.elem[s.top]:= x; RETURN(true)] ENDF;{push_stack }

  7. 3.1 栈的表示和实现 出栈函数pop(S)的实现 算法思想:若栈空则返回空元素NULL; 否则返回栈顶元素。 FUNC pop_stack (VAR s:sqstktp):elemtp; IF s.top = 0 THEN RETURN(NULL) ELSE [ s.top:=s.top -1; RETURN( s.elem[s.top +1] ) ] ENDF;{pop_stack }

  8. 3.1 栈的表示和实现 2. 链栈的存储结构描述 TYPE pointer =↑nodetype; nodetype = RECORD data:elemtp; next:pointer END; linkstktp = pointer;

  9. m 1 空闲栈 栈2 栈1 bot2 top2 top1 bot1 3.1 栈的表示和实现 四. 多个栈共享存储空间问题 1. 两个栈共享存储空间 • 常采用将两个栈底设在可用空间的两端 • 仅当两个栈顶相遇时,才产生上溢,即所有可用空间已用完 • 对每个栈可用的最大空间就可能大于整个可用空间的一半m/2

  10. 3.1 栈的表示和实现 2. n个栈共享存储空间 存储空间的一种分配方法是: • 先将m 个存储单元平均分配给n个栈: S[i].top=S[i].bot=(i-1)*└m/n┘1≤i ≤ n S[n+1].bot = m ( 虚设第n+1个栈的bot为m) • 当某个栈发生上溢,而整个空间未被占满时,进行“浮动”再分配, 第i 个栈上溢条件为: S[i].top = S[i+1].bot 1≤i ≤ n

  11. V[m] V[0] ...... 栈1 栈2 栈n S[n+1].bot S[2].bot .top S[n].bot .top S[1].bot .top 3.1 栈的表示和实现 2. n个栈共享存储空间

  12. 3.1 栈的表示和实现 “浮动”再分配算法思想: • 求n 个栈所占空间总和 SUM:= (S[n].top-S[n].bot)+(S[n-1].top-S[n-1].bot)+…+(S[1].top-S[1].bot) • 若SUM=m,则无可用空间,上溢处理 • 否则进行搜索 若存在某个栈f ,有S[f].top<S[f+1].bot 则移动各元素,得到一个可用单元

  13. 3.2 栈的应用--表达式求值 一. 表达式 表达式由操作数、运算符和界限符组成。 • 操作数(operand):常数或变量 • 运算符(operator) 算术运算符:+、-、*、/、**等  关系运算符:<、≤、=、≠、≥、>  逻辑运算符:AND、OR、NOT • 界限符(delimiter): 左右括号、表达式结束符#等

  14. θ2 θ1 + - * / ( ) # > > < < < > > > > < < < > > > > > > < > > > > > > < > > < < < < < = > > > > > > < < < < < = + - * / ( ) # 3.2 栈的应用--表达式求值 二. 算符优先关系表 • 先乘除,后加减: *>+ • 先括号内,后括号外:(<+、)>一切算符  • 同级按左结合律:   +<+ • 空格为不允许的: )(、(#、#)

  15. 3.2 栈的应用--表达式求值 三. 算术表达式求值的算符优先算法 1.使用DS 算符栈OPTR:有效算符;   操作数栈OPND:有效操作数,运算结果。 2.算法思想  初始化:OPND置为空栈,将#放入OPTR栈底.  依次读入表达式中的每个字符,若是操作数,则入OPND栈; 若是算符,则和OPTR栈顶算符进行优先级比较: • 若栈顶算符优先,则执行相应运算,结果存入OPND栈 •   若与栈顶算符相等,则作()或##处理。 •   若栈顶算符低于,该算符入OPTR栈。  重复(2),直到表达式求值完毕 (读入的是#,且OPTR栈顶元素也为#)

  16. 〖Example〗利用算符优先算法对3*(7-2) 求值 步骤 OPTR栈 OPND栈 输入字符 主要操作 1 # 3*(7-2)# PUSH(OPND,’3’) 2 # 3 *(7-2)# PUSH(OPTR,’*’) 3 # * 3 (7-2)# PUSH(OPTR,’(’) 4 # * ( 3 7-2)# PUSH(OPND,’7’) 5 # * ( 3 7 -2)# PUSH(OPTR,’-’) 6 # * ( - 3 7 2)# PUSH(OPND,’2’) 7 # * ( - 3 7 2 )# operate(‘7’,’-’,’2’) 8 # * ( 3 5 )# POP(OPTR) 9 # * 3 5 # operate(‘3’,’*’,’5’) 10 # 15 # RETURN(GETTOP(OPND))

  17. 3.2 栈的应用--表达式求值 3.算法描述 FUNC exp_reduced :operandtype; INISTACK(OPTR);PUSH(OPTR,’#’);INISTACK(OPND); read (w); WHILE NOT (w=‘#’ AND GETTOP(OPTR)=‘#’) DO IF w NOT IN op THEN PUSH(OPND,w) ELSE CASE precede(GETTOP(OPTR),w) OF ‘<’:[PUSH(OPTR,w);read(w)]; ‘=’:[x:=POP(OPTR);read(w)]; ‘>’:[theta:=POP(OPTR); b:=POP(OPND); a:=POP(OPND); PUSH(OPND,operate(a ,theta,b))] ENDC; RETURN(GETTOP(OPND)) ENDF;{exp_reduced}

  18. 递归是栈的另一个重要应用,也是程序设计的一个强有力的工具。递归是栈的另一个重要应用,也是程序设计的一个强有力的工具。 1. 应用递归的原因和领域 • 递归定义1 当n = 0   阶乘  n!= n*(n-1)! 当n > 0 0 n = 0 Fibonacci数列 Fib(n)= 1 n = 1 Fib(n-1)+Fib(n-2) n > 1 3.3 递归过程

  19. 3.3 递归过程 • 递归结构 后面将要介绍的二叉树,广义表结构本身固有递归特性,操作也可递归描述。 • 用递归求解更简单

  20. 3.3 递归过程 例:计算两个非负整数a*b的算法 1. 递归方式:a*b = b+ (a-1)*b FUNC mul1(a,b:integer):integer; IF a = 0 THEN RETURN(0) ELSE RETURN( b + mul1( a-1,b)) ENDF;{mul1} 2. 迭代方式:a*b = a个b之和 FUNC mul2(a,b:integer):integer; z:= 0; FOR i:=1 TO a DO z:= z + b; RETURN(z) ENDF;{mul2}

  21. 3.3 递归过程 2.递归过程的特点:是程序设计的一个强有力的工具,它具有结构清晰,程序易编、易读、易调试,程序正确性易证明等优点;但运行效率低。 3.基本原理:基本原理是重复地把问题转化为与原问题相似的新问题,直到问题可解决为止。 4.关键点:①用较简单的新问题来表示较复杂的原问题 例如 :n!= n(n-1)!,或 n! = (n+1)!/(n+1) 前者(n-1)!较原问题n!简单,可行;而后者(n+1)!较n!更复杂,不可行。 ②不能产生自己调用自己的无穷序列,即必须有一个递归调用序列的“出口”,来终止递归调用。 5.实现:递归过程都是通过栈来实现的,并且任何递归算法均可通过栈改写为非递归算法。

  22. 3.3 递归过程 X Y Z 1 n 6. 汉诺(Hanoi) 塔问题 设:有X、Y、Z三个塔座,在X上按直径大小递减次序依次插有n个直径各不相同的圆盘,各圆盘按直径从小到大编为1~n 要求:将X 塔上的n个圆盘按规则移至Z上,并仍按同样顺序叠排 移动规则: ①每次只能移动一个圆盘;②移动的圆盘可以插在任一塔座上,但是在任一时刻都不能将大盘压在小盘上。

  23. 3.3 递归过程 算法思想:当n=1:只需将该圆盘从X移到Z即可; 当n>1:若能将压在n号盘上的n-1个圆盘按规则移至Y, 然后把n号盘从X移到Z上,最后再将Y上的n-1个圆盘按规则移至Z上。 原问题为:hanoi(n,X,Y,Z) 化简为:hanoi(n-1,X,Z,Y) move(X,n,Z) 把X上的n号盘移到Z上 hanoi(n-1,Y,X,Z) PROC hanoi(n:integer;X,Y,Z:char); IF n=1 THEN move(X,n,Z) ELSE [ hanoi(n-1,X,Z,Y); move(X,n,Z); hanoi(n-1,Y,X,Z) ] ENDP;{hanoi}

  24. 第三章 习题 1.有A、B、C、D四个元素依次入栈,假设栈足够大,并且已入栈的元素可以在任意时刻出栈,试写出所有可能的出栈序列。 ★ ★ ★ 试推导出:求n个元素所有可能出栈序列的计算公式。 (1)14种, (2)2 2.设带头结点的线性表中元素值为非零正整数,试写出: (1)求线性表中所有元素值之和的递归函数(空表返回0) (2)求线性表中元素最大值的递归函数(空表返回0) (3)求线性表中元素个数的递归函数(空表返回0)

  25. 3.4 队列的表示与实现 rear (tail) 一、定义 4 队列(queue)是限定仅在一端插入,另一端删除的线性表。允许插入的一端叫队尾( rear),允许删除的一端叫队头(front), 不含元素的空表称为空队列 队列的运算特性是先进先出(First In First Out--FIFO) 3 2 front (head) class Queue { public: Queue( ); bool empty( ) const; Error_code enqueue( const Queue_entry &x ); Error_code dlqueue( ); Error_code gethead( Queue_entry &x ) const; // Additional members will represent queue data }; 1 push pop gettop 2 1 2 1

  26. 3.4 队列的表示和实现 二、队列的基本运算 INIQUEUE(Q) 初始化操作,设置一个空队列 EMPTY(Q)判定队列是否为空函数(true/false) ENQUEUE(Q,x)入队列操作,队尾插入元素x DLQUEUE(Q)出队函数 GETHEAD(Q)取队头元素且删除 CLEAR(Q)队列置空操作 CURRENT-SIZE(Q)求队列Q当前元素个数

  27. 3 3 2 2 1 1 0 1 2 3 4 5 6 • 对队列的操作 固定 1. 实际模型 2. 线性模型 Job 1 Job 2 Job 3 Job 4 Job 5 Job 6 Job 7  append Job 1  append Job 2  append Job 3  pop Job 1  append Job 4  append Job 5  append Job 6  pop Job 2  append Job 7  append Job 8

  28. 队头指针q.front < 队尾指针q.rear 3.4 队列的表示和实现 三、队列的链式存储结构——链队列 1.存储结构 链队列需要队头和队尾两个指针来确定。 TYPE queueptr = queuenode queuenode = RECORD data:elemtp; next:queueptr END; linkedquetp = RECORD front,rear:queueptr END; 给链队列添加个头结点,并令头指针指向头结点。 空链队列有头尾指针均指向头结点 即 q.front = q.rear . . .

  29. 3.4 队列的表示和实现 2.出队算法 FUNC dl_linkedque(VAR q:linkedquetp):elemtp; IF q.front=q.rear THEN RETURN(NULL) ELSE [s:= q.front↑.next; q.front↑.next:=s↑.next; IF s↑.next=NIL THEN q.rear:=q.front; x:=s↑.data; dispose(s); RETURN(x)] ENDF; {dl_linkedque} 删除时的三种情形: a.删除前已空; b.删除前只有一个结点,删除后为空队列; c.其他情形(删除前结点数>1) 思考:在什么情况下出队要修改q.rear (队尾)指针?为什么?

  30. 3.4 队列的表示和实现 3 .入队算法 PROC en_linkedque(VAR q:linkedquetp; x:elemtp); new(p); p↑.data:= x; p↑.next:= NIL; q.rear↑.next:= p;q.rear:= p ENDP; 思考: 如果将空队列定义为q.front↑.next = NIL, 出队和入队算法需要做哪些修改?

  31. 3.4 队列的表示和实现 四、队列的顺序存储结构 1. 一般顺序存储结构 用一个向量来存放队列元素,并用两个指针来指示队头和队尾。并约定:头指针sq.front总是指向队头元素的前一个位置;尾指针sq.rear指向队尾元素。 TYPE sequeuetp = RECORD elem:ARRAY[1..maxsize] 0F elemtp; front,rear : 0..maxsize END;

  32. [ 0 ] [ 0 ] [ 5 ] [ 5 ] [ 4 ] [ 4 ] [ 1 ] [ 1 ] [ 3 ] [ 3 ] [ 2 ] [ 2 ] 2. 循环队列  append Job 1 Job 5 Job 6  append Job 2 empty or full ?  append Job 3 Job 4 Job 1 Job 7  pop Job 1  append Job 4 Job 8 Job 3 Job 2  append Job 5  append Job 6  pop Job 2 i = ( i + 1 ) % max  append Job 7 append := rear “++” then append job  append Job 8 pop := remove job then front “++”

  33. [ 0 ] [ 5 ] [ 4 ] [ 1 ] [ 3 ] [ 2 ] 可能的解决办法: 1. 在队列中预留一个空位 2. 设定一个标志 3. 设置一个数字计数器 4. 让front和rear等于一个不可能等于的值

  34. 方法四:扩大rear和front的定义域为0..maxsize。 • 初值rear=0;front=maxsize • 入队前,先判rear是否=maxsize,是则为队满。 • 当入队后,使得cq.rear=cq.front, 则令cq.rear=maxsize,表示队满。 若cq.front=maxsize,则cq.front:=cq.rear-1 • 出队前,先判front是否=maxsize,是则为队空。 • 当出队后,使得cq.front=cq.rear, 则令cq.front:=maxsize,表示队空。 若cq.rear=maxsize,则cq.rear:=cq.front-1

  35. 习 题 1.编写对如下存储结构的线性表进行倒置的算法,线性表(a1,a2,…,an)的倒置为(an,…,a2,a1)。(1)以一维数组为存储结构; (2)以单链表为存储结构; (3)以双向链表为存储结构;

  36. 串的概念:n个字符的有限序列 记为:S=‘a1a2……an’ (n>=0) 第四章 串 • S是串名,用单引号括起来的字符序列是串的值 • 串中字符个数n称为串的长度 • 长度为0的串称为空串。如:串S=‘This is a string.’ • ‘This’是一个子串,‘string’也是一个子串 求子串在串中的起始位置称为子串定位或模式匹配

  37. 基本操作: ASSIGN(S,T) ::= 赋值操作 设S,T为串名,T=‘abc’,则结果 S= ‘abc’ CREATE(S,ss) ::= 赋值操作 如:CREATE(S,’abc’)则 S=‘abc’ EQUAL(S, T)::= 判等函数返回真或假 LENGTH(S) ::= 求长度函数 如:LENGTH(T)=3 CONCAT(S,T) ::= 联结函数如:S =‘xyz’ T=‘abc’则 CONCAT(S,T)=‘xyzabc’ CONCAT(S,T)<>CONCAT(T,S) SUBSTR(S,start, len) ::= 求子串函数 取从S中第start个字符起,长 度为len的字符序列。 如:1<=start<=length(s)+1 0<=len<=length(s)-start+1

  38. 基本操作: INDEX(S,T) ::= 定位函数 若主串S中存在和T相等的子串, 则函数值为S中第一个等于T的子串在主串S 中的位置;否则,函数值为0 REPLACE(S,T,V) ::= 置换操作 以串V替换在串S中与串T相等的 所有子串 如:S=‘BBABBABBA’ T=‘AB’ V=‘C’ 结果为:S=‘BBCBCBA’

  39. D D A T A A T A 串的存储结构 顺序存储结构 CONST maxlen=串被确认的最大长度 TYPE strtp=RECORD ch:ARRNY [1..maxlen] OF char; curlen:0..maxlen END; PACKED 非紧缩格式 紧缩格式

  40. 串的存储结构 链式存储结构 CONST chunksize=用户定义的结点大小: TYPE pointer= ↑ chunk chunk=RECORD Oh:ARRAY[1..chunksize] OF char; next:pointer END; linkstringtp=RECORD head,tail:pointer; length:integer END;

  41. head tail D A T A NIL tail head D A T A NIL 串的存储结构 块链结构

  42. 第五章 数组和广义表 5.1 数组和广义表的定义 5.2 数组和广义表的基本运算 5.3 数组的存储结构 5.4 广义表的存储结构

  43. 5.1 数组和广义表的定义 数组和广义表可以视为是线性表的扩展,即线性表中的数据元素本身也是一个数据结构。 ⒈ 二维数组定义 ┌a11a12…a1n ┐ Amxn= │ a21 a22…a2n │ │ ·· │ │ ·· │ └ am1am2…amn┘ Amxn可定义为一个线性表 A=(a1,a2,…,an), 其中: 每个数据元素aj(j=1,2,…n)又是一个列向量的线性表, 即:aj=(a1j,a2j,…,amj), 1≤j≤n

  44. 5.1 数组和广义表的定义 或者二维数组Amxn可定义为一个线性表: A=(B1,B2,…,Bm) 其中:每个数据元素Bj又是一个行向量的线性表,即: Bj=(aj1,aj2,…,ajn), 1≤j≤m 即A =(B1,B2,…,Bm) =((a11,a12,…,a1n),(a21,a22,…,a2n),…,(am1,am2,…,amn)) ┌ a11 a12… a1n ┐ Amxn= │ a21 a22… a2n │ │ ·· │ │ ·· │ └ am1 am2 … amn┘ 类似地,可以定义n维数组。

  45. 5.1 数组和广义表的定义 ⒉ 广义表定义 广义表可定义为:数据元素可以是表的线性表。 记为:LS=(d1,d2,…,dn) LS为表名, di (i=1,2,…,n),可以是单元素(用小写字母表示); 也可以是广义表(称为子表,用大写字母表示); n为表的长度,当长度为0时称为空表; 称非空表的第一个元素d1为表头, 其余元素组成的表(d2,…,dn)称为表尾。

  46. 5.2 数组和广义表的基本运算 ⒈ 数组的基本运算 ⑴ 给定下标,存取相应的数据元素; ⑵ 给定下标,修改相应的数据元素; ⒉ 广义表的基本运算 ⑴ 取表头 HEAD(LS); ⑵ 取表尾 TAIL(LS)。

  47. 5.3 数组的存储结构 ⒈ 数组的顺序存储结构 由于数组不作插入、删除操作,所以常采用顺序存储结构来实现数组,即用一组连续的存储单元,以行主(或列主)顺序依次存放数组的数据元素。 设每个数据元素占L个存储单元,di为第i维的下标上界,ci为第i维的下标下界,采用行主顺序存放,则二维数组中任一元素aij的存储地址为: Loc[i,j]=Loc[c1,c2]+{(i-c1)×(d2-c2+1)+(j-c2)}×L

  48. Address Content …… …… array+i itemi array+i+1 itemi+1 …… …… (1) Array : array[ i ] = itemi 顺序存储 不需要移动数据元素,我们可以  Finding length  Reading list  Retrieving item  Replacing item Trouble-makers:  Insertion  Deletion (2) Non-sequential mappings : will be discussed later

  49. 稀疏矩阵(Sparse Matrices) Wow! I see how much space I could have wasted. But, but do people really deal with such kind of matrices? What is a SPARSE matrix? What’s wrong with my simple representation A[MAX_ROWS][MAX_COLS]? Well, unlike other math concepts, there is no definite line drawn here. To give you some idea, think of a 1000 by 1000 matrix with 3000 nonzero entries. Unfortunately we do. It’s quite common to see sparse matrices in numerical analysis. So I’d better figure out another way to represent it. Hmm..... A matrix is SPARSE if it contains many zero entries. Many? How many is “many”?

  50. 5.3 数组的存储结构 2. 矩阵的压缩存储 • 给多个值相同的元素只分配一个存储单元, • 或者对值为零的元素不分配存储单元。 ⑴ 对称矩阵:满足 aij= aji, 1≤i,j≤n 可以只存储n阶对称矩阵的上(或下)三角部分,将存储量从n2压缩到n(n+1)/2 。 若用一维数组S(1··n(n+1)/2) ,以行主顺序存放下三角部分,则S[k]和矩阵元素aij的对应关系为: ┌ i(i+1)/2 + j 当i≥j k= │ └ j(j-1)/2 + i 当i<j

More Related