Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
通常称,栈和队列是限定插入和删除 只能在表的 “ 端点 ” 进行的线性表。  线性表                  栈               队列Insert(L, i, x)   Insert(S, n+1, x)   Insert...
第三章 栈和队列 3.1 栈 (stack) 3.2 栈的应用举例 3.4 队列 (Queue)
学习提要:1. 掌握栈和队列这两种抽象数据类型的特点,    并能在相应的应用问题中正确选用它们。2. 熟练掌握栈类型的两种实现方法,即两种存    储结构表示时的基本操作实现算法,特别应 注意栈满和栈空的条件以及它们的描述方法。3. 熟练掌握...
§3.1 栈( stack )3.1.1 栈的类型定义3.1.2 栈的表示和实现
3.1.1 栈的类型定义 栈的定义和特点  定义:限定仅在表尾进行插入或删   除操作的线性表,表尾 — 栈顶,表头   — 栈底,不含元素的空表称空栈。     进栈           出栈    栈     ...          ...
 栈的类型定义ADT Stack {  数据对象:  D = { ai | ai ∈ElemSet, i=1,2,...,n, n≥0 }   数据关系:   R1 = { <ai-1, ai >| ai-1, ai∈D, i=2,...,n...
InitStack(&S)                 DestroyStack(&S)StackLength(S)                 StackEmpty(s)GetTop(S, &e)                 Cl...
3.1.2 栈的表示和实现    顺序栈          类似于线性表的顺序映象实      现,指向表尾的指针可以作为栈      顶指针。//----- 栈的顺序存储表示 -----#define STACK_INIT_SIZE 100...
实现:一维数组 s[M]                                 栈满          栈空                top            5   top      F   5              ...
Status InitStack (SqStack &S){// 构造一个空栈 SS.base=(SElemType*)malloc(STACK_INIT_SIZ                       E *sizeof(SElemTyp...
Status Push (SqStack &S, SElemType e) { if (S.top - S.base >= S.stacksize) {// 栈满,追加存储空间     S.base = (SElemType *) reallo...
Status Pop (SqStack &S, SElemType &e) {   // 若栈不空,则删除 S 的栈顶元素,   // 用 e 返回其值,并返回 OK ;   // 否则返回 ERROR  if (S.top == S.base...
链栈    栈的链式存储结构。栈顶指针 就是链表的头指针。栈顶指针       an   an-1   a1 ∧ 注意 : 链栈 中指针的方 向
 入栈操       作       top top                                 栈底p      x                  …...    ^    p->next=top ; top=p  ...
§3.2 栈的应用3.2.1   数制转换3.2.2   括号匹配的检验3.2.3   行编辑程序问题3.2.4   迷宫求解3.2.5   表达式求值
3.2.1 数制转换十进制 N 和其他 d 进制数的转换原理 : N=( N div d )*d + N mod d其中: div 为整除运算, mod 为求余运算
例如: (1348)10=(2504)8 ,其运算过程如下:   N     N div 8       N mod 输                             8计算 1348                       出 ...
void conversion( ) {     initstack(S); // 构造空栈     scanf (“%d”,N);     while(N){        push(S , N%8);        N=N/8; }    ...
3.3.2 括号匹配的检验 假设在表达式中 ([]())或[([ ][ ])] 等为正确的格式, [( ])或([( ))或 (() ] ) 均为不正确的格式。    则 检验括号是否匹配的方法可用“ 期待的急迫程度 ” 这个概念来描述。
例如:考虑下列括号序列:   [ ( [ ] [ ] ) ]   1 2 34 5 6 7 8分析可能出现的不匹配的情况 :到来的右括弧 并非是所 “ 期待 ” 的;直到结束,也没有到来所“ 期待 ” 的括弧。
算法的设计思想:1 )凡出现左括弧,则进栈;2 )凡出现右括弧,首先检查栈是否空   若栈空,则表明该“ 右括弧” 多余,   否则和栈顶元素比较,     若相匹配,则“ 左括弧出栈” ,     否则表明不匹配。3 )表达式检验结束时,  ...
3.2.3 行编辑程序问题                不恰 如何实现?          当!“ 每接受一个字符即存入存储器 ”    ?合理的作法是:   设立一个输入缓冲区,用以接受用户输入的一行字符,然后逐行存入用户数据区,并假设 “...
假设从终端接受了这样两行字符:  whli##ilr#e ( s#*s)   outcha@putchar(*s=#++);则实际有效的是下列两行:  while (*s)  putchar(*s++);
whli##ilr#e ( s#*s)                 #    #       #                          r   r      i          i        l   l      l   ...
whli##ilr#e ( s#*s)                          EOF                 #    )    )                      s    s     s            ...
Void LineEdit( ){// 利用字符栈 S, 从终端接收一行并传送至调用过程的数据   区  InitStack(S); // 构造空栈  ch = getchar(); // 从终端接受一个字符  While(ch!= EOF){...
while (ch != EOF) { //EOF 为全文结束符 while (ch != EOF && ch != ‘n’) { // ‘ n ’ 为换行符   switch (ch) {    case # : Pop(S, c); bre...
3.2.4 迷宫求解通常用的是“ 穷举求解 ” 的方法   #   # # # # # # # #   #   #   →↓ # $ $ $ #      #   #     ↓ # $ $ $ #     #   #   ↓ ←$ $ # #...
求迷宫路径算法的基本思想是:若当前位置“ 可通 ” ,则纳入 路径,继续前进 ;若当前位置“ 不可通 ” ,则后 退,换方向继续探索 ;若四周“ 均无通路 ” ,则将当前 位 置从路径中删除出去。
“ 下一个位置 ” 指 “ 当前位置 ” 四周 “ 东南西北 ” 四个方向上相临的通道块 。         北     西       东         南             当前位置
根据算法思想 , 为了保证在任何位置上都能沿原路退回 , 需要用一个后进先出的结构来保存从入口到当前位置的路径 .     假设以栈 S 记录 “ 当前路径 ” ,则栈顶中存放的是 “ 当前路径上最后一个通道块 ” 。由此,“ 纳入路径 ” 的...
算法描述设定当前位置的初值为入口位置do {     若当前位置可通     则 { 将当前位置插入栈顶 ; // 纳入路径        若该位置是出口位置 , 则结束 ; // 求得路径存 放在栈中        否则切换当前位置的东相邻方...
算法描述否则  // 不通若栈不空且栈顶位置尚有其他方向未探索 ,  则设定新的当前位置为沿顺时针方向旋转找 的栈顶位置的下一相邻块 ;       栈顶位       当前位        置         置
算法描述若栈不空但栈顶位置的四周均不可通则 { 删去栈顶位置 ;         若栈不空 , 则重新测试新的栈顶位      置,         直到找到一个可通的相邻块或出栈      至栈空 ;   }}while( 栈不空 );   ...
算法描述说明 : 当前位置可通 , 指未曾走到过的通道块 , 即要求该通道块既不在当前路径上 , 也不是曾经纳入过路径的通道块 .     a起点                    a-b-c-f-g ( 简单路径 )           ...
通道块的数据描述Type struct {int ord; // 通道块在路径上的 “ 序号 ”PosType seat; // 通道块在迷宫中的 “ 坐标 ”int   di;   // 从此通道块走向下一通道块的 “ 方 向”} SElem...
迷宫问题算法Status MazePath(MazeType maze,PosType start, PosTypeend){// 若迷宫 maze 中存在从 start 到 end 的通道,则求得一条存放在栈中,并返回 true ;否则返回 ...
迷宫问题算法do {       if (Pass(curpos)) { // 当前位置可通            FootPrint(curpos); // 留下足迹            e = ( curstep,curpos,1);  ...
迷宫问题算法else { // 当前位置不能通过If ( !StackEmpty(s)) {      Pop(S,e) ; // 出栈,弹出当前的栈顶位置至 e      while(e.di == 4 && !StackEmpty(s)) ...
迷宫问题演示  成功     不成功
3.2.5 表达式求值限于二元运算符的表达式定义 : Exp = S1 OP S2操作数 : 变量、常量、表达式运算符 : 算术运算符、关系运算符、          逻辑运算符界限符:括号、结束符
算法思想:      设置两个工作栈, OPTR 存运算符, OPND 存操作数及运算结果。算法运算思想:1. 首先置 OPND 栈为空, “ #” 为运算符栈底元素。2. 依次读入表达式中的每个字符,若是操作数进 OPND 栈 , 若是运算符...
例: 3 * ( 7 – 2 ) #    C C C C C C C   C                          -          2               (      7-2          5         ...
例: 3 * ( 7 – 2 ) OPTR 栈    OPND 栈        输入                     操作1 #                 3*(7–2)#   PUSH( OPND, ‘3’ )2 #     ...
例: 3 * ( 7 – 2 ) OPTR 栈    OPND 栈        输入                     操作1 #                 3*(7–2)#   PUSH( OPND, ‘3’ )2 #     ...
例: 3 * ( 7 – 2 ) OPTR 栈    OPND 栈        输入                     操作1 #                 3*(7–2)#   PUSH( OPND, ‘3’ )2 #     ...
OperandType EvaluateExpression() { // 设 OPTR 和 OPND 分别为运算符栈和运算数栈, OP 为运算符集合。 InitStack (OPTR); Push (OPTR, #); initStack (...
switch ( precede(GetTop(OPTR), c) {     case <: // 栈顶元素优先权低          Push(OPTR, c); c = getchar();          break;     cas...
算法演示( 3+5 ) * 2- 12 + 6 / 3 #
第三章作业3.1 设将整数 1 、 2 、 3 、 4 依次进栈,但只要出栈时栈非空,则可将出栈操作按任何次序夹入其中,请回答下列问题:     ( 1 )若入栈次序为push(1) , pop() , push(2 ), push(3) , ...
§3.4 队列3.4.1 队列的类型定义3.4.2 链队列3.4.3 循环队列
3.4.1 队列的类型定义队列是限定只能在表的一端进行插入, 在表的另一端进行删除的线性表。出      a1     a2   a3…………………….an      入队队     front                    rear...
队列的类型定义ADT Queue {  数据对象:      D = {ai | ai∈ ElemSet, i=1,2,...,n, n≥0}   数据关系:     R1 = { <a i-1,ai > | ai-1, ai ∈ D, i=...
队列的基本操作:InitQueue(&Q)     DestroyQueue(&Q)QueueEmpty(Q)     QueueLength(Q)GetHead(Q, &e) ClearQueue(&Q)EnQueue(&Q, e) DeQu...
3.4.2 链队列-队列的链式表示和                实现   typedef struct QNode{// 结点类型   QElemType     data ;   struct QNode *next ;  }QNode,...
Q.front             a1       …   an   ∧Q.rear          Q.front  空队                     ∧          Q.rear  列
空 队               ^               Q.rear -> next=p        front rear       p          Q.rear=px 入队                 x      ...
Status InitQueue (LinkQueue &Q) {    // 构造一个空队列 Q    Q.front = Q.rear =             (QueuePtr)malloc(sizeof(QNode));    if...
Status EnQueue (LinkQueue &Q,                            QElemType e) {   // 插入元素 e 为 Q 的新的队尾元素   p = (QueuePtr) malloc (s...
EnQueue                          p            ...      ∧      e        ∧  Q.front         Q.rear    Q.rear p->data = e; p-...
Status DeQueue (LinkQueue &Q,                       QElemType &e) { // 若队列不空,则删除 Q 的队头元素, // 用 e 返回其值,并返回 OK ;否则返回ERROR if...
DeQueue       p   e          ...                         ∧  Q.front                             Q.rear p = Q.front->next; ...
3.4.3 循环队列-队列的顺序表示和实现#define MAXQSIZE 100 // 最大队列长度typedef struct {  QElemType *base; // 动态分配存储空间  int front; // 头指针,若队列不空...
实现:用一维数组实现 sq[M]                                               rear           5                5              5          ...
解决方案 1  队首固定,每次出队剩余元素向下移  动 , 避免发生假溢出 .Rear         Rear        J6           J6        J5           J5   Rear    J6     ...
解决方案 2 循环队列         基本思想:把队列设想成环形,让        sq[0] 接在 sq[M-1] 之后,若 rear+1==M,        则令 rear=0;M-1       J6      Rear    M...
解决方案 2循环队列 实现: 利用 “ 模 ” 运算,实现若 rear+1==M, 则 rear=0 的设定 ;             入队:        sq[rear]=x;              rear=(rear+1)...
front  队空: fro nt==re a r                               rear  队满: fro nt==re a r                                        4 ...
解决方案:1. 另外设一个标志位以区别队空、队满2. 少用一个元素空间:      队空: front==rear 队满:(rear+1)%M==front              front                rear     ...
分析可见 , 在 C 语言中不能用动态分配的一维数组来实现循环队列 . 如果用户的应用程序中设有循环队列 , 则必须为它设定一个最大队列长度 ; 若用户无法预估所用队列的长度 , 则宜用链队列 .
Status InitQueue (SqQueue &Q) {  // 构造一个空队列 Q  Q.base = (QElemType *) malloc        (MAXQSIZE *sizeof (QElemType));   if (...
Status EnQueue (SqQueue &Q, QElemTypee) { // 插入元素 e 为 Q 的新的队尾元素  if ((Q.rear+1) % MAXQSIZE == Q.front)      return ERROR; ...
Status DeQueue (SqQueue &Q, QElemType &e){ // 若队列不空,则删除 Q 的队头元素,    // 用 e 返回其值,并返回 OK; 否则返回 ERROR    if (Q.front == Q.rea...
例 . 写出以下程序段的输出结果(队列中的元素         类型 QElemType 为 char )。Void main( ){  Queue Q; InitQueue(Q);  Char x=‘e’, y=‘c’;  EnQueue(Q...
括号匹配的检验1 )凡出现左括弧,则进栈;2 )凡出现右括弧,首先检查栈是否空   若栈空,则表明该“ 右括弧” 多余,   否则和栈顶元素比较,   若相匹配,则“ 左括弧出栈” ,   否则表明不匹配。3 )表达式检验结束时,   若栈空,...
括号匹配的检验Status check( ) { // 对于输入的任意一个字符串 , 检验括号是否配对SqStack s;SElemType ch[80], *p,e; //c h 存放字符串InitStack(s); // 初始化栈成功Get...
括号匹配的检验while(*p) // 没有到串尾Switch(*p){case ‘(’, ‘[’ , ‘{’ :           Push(s,*p++); break; // 左括号入栈且 p++case ‘)’, ‘]’ , ‘}’ ...
括号匹配的检验{ printf(“ 左右括号不匹配 n”);    return ERROR;}p++;}else {// 栈空       printf( “ 缺乏左括号 n”);       return ERROR;} //end ifd...
括号匹配的检验if (StackEmpty(s)) // 字符串结束时栈空  printf(“ 括号匹配 n”);else printf(“ 缺少右括号 n”);}//end check
Upcoming SlideShare
Loading in …5
×

第三章 栈和队列(新)

195 views

Published on

严蔚敏 - 数据结构

Published in: Technology
  • Be the first to comment

  • Be the first to like this

第三章 栈和队列(新)

  1. 1. 通常称,栈和队列是限定插入和删除 只能在表的 “ 端点 ” 进行的线性表。 线性表 栈 队列Insert(L, i, x) Insert(S, n+1, x) Insert(Q, n+1, x) 1≤i≤n+1 Delete(L, i) Delete(S, n) Delete(Q, 1) 1≤i≤n栈和队列是两种常用的数据类型
  2. 2. 第三章 栈和队列 3.1 栈 (stack) 3.2 栈的应用举例 3.4 队列 (Queue)
  3. 3. 学习提要:1. 掌握栈和队列这两种抽象数据类型的特点, 并能在相应的应用问题中正确选用它们。2. 熟练掌握栈类型的两种实现方法,即两种存 储结构表示时的基本操作实现算法,特别应 注意栈满和栈空的条件以及它们的描述方法。3. 熟练掌握循环队列和链队列的基本操作实现
  4. 4. §3.1 栈( stack )3.1.1 栈的类型定义3.1.2 栈的表示和实现
  5. 5. 3.1.1 栈的类型定义 栈的定义和特点 定义:限定仅在表尾进行插入或删 除操作的线性表,表尾 — 栈顶,表头 — 栈底,不含元素的空表称空栈。 进栈 出栈 栈 ... an 顶 ……... 栈 s=(a1,a2,……,an) a2 栈底 a1特点:先进后出( FILO )或后进先出( LIFO )
  6. 6.  栈的类型定义ADT Stack { 数据对象: D = { ai | ai ∈ElemSet, i=1,2,...,n, n≥0 } 数据关系: R1 = { <ai-1, ai >| ai-1, ai∈D, i=2,...,n } 约定 an 端为栈顶, a1 端为栈底。 基本操作: } ADT Stack
  7. 7. InitStack(&S) DestroyStack(&S)StackLength(S) StackEmpty(s)GetTop(S, &e) ClearStack(&S)Push(&S, e) Pop(&S, &e)StackTravers(S, visit())
  8. 8. 3.1.2 栈的表示和实现 顺序栈 类似于线性表的顺序映象实 现,指向表尾的指针可以作为栈 顶指针。//----- 栈的顺序存储表示 -----#define STACK_INIT_SIZE 100; // 存储空间初始分配量#define STACKINCREMENT 10;// 存储空间分配增量typedef struct { SElemType *base; // 栈底指针 SElemType *top; // 栈顶指针 int stacksize; // 栈的当前可使用的最大容量} SqStack;
  9. 9. 实现:一维数组 s[M] 栈满 栈空 top 5 top F 5 5 4 top E 4 4 3 top D 3 3 top 2 2 top C 2 top B 1 1 top B 1top top A 0base 0 top A 0 base base 出栈 空栈 进栈栈底指针 b a s e , 始 设数组维数为 M终指向栈底位置; to p =b a s e , 栈空,此时出栈,栈顶指针 to p , 其初 则下溢( und e rflo w)值指向栈底,始终在栈顶元素的下一 to p =M, 栈满,此时入栈,则个位置 上溢( o ve rflo w)
  10. 10. Status InitStack (SqStack &S){// 构造一个空栈 SS.base=(SElemType*)malloc(STACK_INIT_SIZ E *sizeof(SElemType)); if (!S.base) exit (OVERFLOW); // 存储分配失败 S.top = S.base; S.stacksize = STACK_INIT_SIZE; return OK;}
  11. 11. Status Push (SqStack &S, SElemType e) { if (S.top - S.base >= S.stacksize) {// 栈满,追加存储空间 S.base = (SElemType *) realloc ( S.base, (S.stacksize + STACKINCREMENT) * sizeof (SElemType)); if (!S.base) exit (OVERFLOW); // 存储分配失败 S.top = S.base + S.stacksize; S.stacksize += STACKINCREMENT; } *S.top++ = e;
  12. 12. Status Pop (SqStack &S, SElemType &e) { // 若栈不空,则删除 S 的栈顶元素, // 用 e 返回其值,并返回 OK ; // 否则返回 ERROR if (S.top == S.base) return ERROR; e = *--S.top; return OK;}
  13. 13. 链栈 栈的链式存储结构。栈顶指针 就是链表的头指针。栈顶指针 an an-1 a1 ∧ 注意 : 链栈 中指针的方 向
  14. 14.  入栈操 作 top top 栈底p x …... ^ p->next=top ; top=p  出栈操 作 q top 栈底top …... ^q=top ; top=top->next; //q 返回了出栈的元素
  15. 15. §3.2 栈的应用3.2.1 数制转换3.2.2 括号匹配的检验3.2.3 行编辑程序问题3.2.4 迷宫求解3.2.5 表达式求值
  16. 16. 3.2.1 数制转换十进制 N 和其他 d 进制数的转换原理 : N=( N div d )*d + N mod d其中: div 为整除运算, mod 为求余运算
  17. 17. 例如: (1348)10=(2504)8 ,其运算过程如下: N N div 8 N mod 输 8计算 1348 出 168 4顺 顺序 168 21 0 序 21 2 5 top top 2 2 0 top 2 5 5 top 0 0 top 4 4 0 4 4
  18. 18. void conversion( ) { initstack(S); // 构造空栈 scanf (“%d”,N); while(N){ push(S , N%8); N=N/8; } while(! Stackempty(s)){ pop(S,e); printf(“%d”,e); }}//conversion
  19. 19. 3.3.2 括号匹配的检验 假设在表达式中 ([]())或[([ ][ ])] 等为正确的格式, [( ])或([( ))或 (() ] ) 均为不正确的格式。 则 检验括号是否匹配的方法可用“ 期待的急迫程度 ” 这个概念来描述。
  20. 20. 例如:考虑下列括号序列: [ ( [ ] [ ] ) ] 1 2 34 5 6 7 8分析可能出现的不匹配的情况 :到来的右括弧 并非是所 “ 期待 ” 的;直到结束,也没有到来所“ 期待 ” 的括弧。
  21. 21. 算法的设计思想:1 )凡出现左括弧,则进栈;2 )凡出现右括弧,首先检查栈是否空 若栈空,则表明该“ 右括弧” 多余, 否则和栈顶元素比较, 若相匹配,则“ 左括弧出栈” , 否则表明不匹配。3 )表达式检验结束时, 若栈空,则表明表达式中匹配正确, 否则表明“ 左括弧” 有余。
  22. 22. 3.2.3 行编辑程序问题 不恰 如何实现? 当!“ 每接受一个字符即存入存储器 ” ?合理的作法是: 设立一个输入缓冲区,用以接受用户输入的一行字符,然后逐行存入用户数据区,并假设 “ #” 为退格符, “ @” 为退行符。
  23. 23. 假设从终端接受了这样两行字符: whli##ilr#e ( s#*s) outcha@putchar(*s=#++);则实际有效的是下列两行: while (*s) putchar(*s++);
  24. 24. whli##ilr#e ( s#*s) # # # r r i i l l l l l i i h h h h h w w w w w
  25. 25. whli##ilr#e ( s#*s) EOF # ) ) s s s s * * ( ( ( ( e e e e l l l l i i i i h h h h w w w w
  26. 26. Void LineEdit( ){// 利用字符栈 S, 从终端接收一行并传送至调用过程的数据 区 InitStack(S); // 构造空栈 ch = getchar(); // 从终端接受一个字符 While(ch!= EOF){ //EOF 为全文结束符 ... // 对输入的字符的判断与处理ClearStack(S); // 重置 S 为空栈if (ch != EOF) ch = getchar();}DestroyStack(S);} //LineEdid
  27. 27. while (ch != EOF) { //EOF 为全文结束符 while (ch != EOF && ch != ‘n’) { // ‘ n ’ 为换行符 switch (ch) { case # : Pop(S, c); break; case @: ClearStack(S); break;// 重置 S 为空栈 default : Push(S, ch); break; } ch = getchar(); // 从终端接收下一个字符 }将从栈底到栈顶的字符传送至调用过程的数据区;
  28. 28. 3.2.4 迷宫求解通常用的是“ 穷举求解 ” 的方法 # # # # # # # # # # # →↓ # $ $ $ # # # ↓ # $ $ $ # # # ↓ ←$ $ # # # # ↓ # # # # # # →→↓ # # # # # →→↓ # # # # # # # ↓ # # # # →→→Θ # # # # # # # # # # #
  29. 29. 求迷宫路径算法的基本思想是:若当前位置“ 可通 ” ,则纳入 路径,继续前进 ;若当前位置“ 不可通 ” ,则后 退,换方向继续探索 ;若四周“ 均无通路 ” ,则将当前 位 置从路径中删除出去。
  30. 30. “ 下一个位置 ” 指 “ 当前位置 ” 四周 “ 东南西北 ” 四个方向上相临的通道块 。 北 西 东 南 当前位置
  31. 31. 根据算法思想 , 为了保证在任何位置上都能沿原路退回 , 需要用一个后进先出的结构来保存从入口到当前位置的路径 . 假设以栈 S 记录 “ 当前路径 ” ,则栈顶中存放的是 “ 当前路径上最后一个通道块 ” 。由此,“ 纳入路径 ” 的操作即为 “ 当前位置入栈 ” ;“ 从当前路径上删除前一通道块 ” 的操作为 “ 出栈 ” 。
  32. 32. 算法描述设定当前位置的初值为入口位置do { 若当前位置可通 则 { 将当前位置插入栈顶 ; // 纳入路径 若该位置是出口位置 , 则结束 ; // 求得路径存 放在栈中 否则切换当前位置的东相邻方块为新的当前 位置 ; } 出口 东 当前位置 当前位置 当前位置
  33. 33. 算法描述否则 // 不通若栈不空且栈顶位置尚有其他方向未探索 , 则设定新的当前位置为沿顺时针方向旋转找 的栈顶位置的下一相邻块 ; 栈顶位 当前位 置 置
  34. 34. 算法描述若栈不空但栈顶位置的四周均不可通则 { 删去栈顶位置 ; 若栈不空 , 则重新测试新的栈顶位 置, 直到找到一个可通的相邻块或出栈 至栈空 ; }}while( 栈不空 ); 当前位 置
  35. 35. 算法描述说明 : 当前位置可通 , 指未曾走到过的通道块 , 即要求该通道块既不在当前路径上 , 也不是曾经纳入过路径的通道块 . a起点 a-b-c-f-g ( 简单路径 ) bd c a-b-c-d-e-c-f-g ( 非简单路径 f ) 若 b 已经纳入过路径 , 则在当e 前位置为 f 时 , 将不作为可通 终点 g 位置考虑 , 否则 , 将形成死循 环.
  36. 36. 通道块的数据描述Type struct {int ord; // 通道块在路径上的 “ 序号 ”PosType seat; // 通道块在迷宫中的 “ 坐标 ”int di; // 从此通道块走向下一通道块的 “ 方 向”} SElemType; // 栈的元素类型
  37. 37. 迷宫问题算法Status MazePath(MazeType maze,PosType start, PosTypeend){// 若迷宫 maze 中存在从 start 到 end 的通道,则求得一条存放在栈中,并返回 true ;否则返回 falseInitStack(S); curpos = start; // 设定当前位置为 “ 入口位置”curstep = 1; // 探索第一步do { ... } while (!StackEmpty(s));return (false);} //MazePath
  38. 38. 迷宫问题算法do { if (Pass(curpos)) { // 当前位置可通 FootPrint(curpos); // 留下足迹 e = ( curstep,curpos,1); Push(S,e); // 入栈,加入足迹 if (curpos == end) return (ture); // 到达出口 curpos = NextPos(curpos,1); // 下一个位置为当前位置的东邻 curstep++; // 探索下一个位置 }// if ...
  39. 39. 迷宫问题算法else { // 当前位置不能通过If ( !StackEmpty(s)) { Pop(S,e) ; // 出栈,弹出当前的栈顶位置至 e while(e.di == 4 && !StackEmpty(s)) { MarkPrint(e.seat); Pop(S,e); // 若该位置无方向可寻,留下不能通过的标记,并退回一步 } //while if (e.di < 4) { // 表示仍有方向可寻 e.di++; Push(S,e); // 换下一方向试探 curpos = NextPos(e.seat, e.di); } //end if}//end else
  40. 40. 迷宫问题演示 成功 不成功
  41. 41. 3.2.5 表达式求值限于二元运算符的表达式定义 : Exp = S1 OP S2操作数 : 变量、常量、表达式运算符 : 算术运算符、关系运算符、 逻辑运算符界限符:括号、结束符
  42. 42. 算法思想: 设置两个工作栈, OPTR 存运算符, OPND 存操作数及运算结果。算法运算思想:1. 首先置 OPND 栈为空, “ #” 为运算符栈底元素。2. 依次读入表达式中的每个字符,若是操作数进 OPND 栈 , 若是运算符则和OPTR 栈顶元素比较优先权后作相应操作,直至整个表达式求值完毕。
  43. 43. 例: 3 * ( 7 – 2 ) # C C C C C C C C - 2 ( 7-2 5 7 * 3*5 15 3 # OPND 栈 OPTR 栈
  44. 44. 例: 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)# PHSH( 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 )“(” < “*” , 因此“ ( ” 入操作符栈 OPTR
  45. 45. 例: 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)# PHSH( 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 )“-” >“ )” , 因此执行 7-2 操作,同时OPTR 栈弹出“ -” , OPND 栈弹出“ 2” 和
  46. 46. 例: 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)# PHSH( 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 )“)” =“(” , 操作符的优先权相等,弹出“ (”,脱括号并接收下一字符 .
  47. 47. OperandType EvaluateExpression() { // 设 OPTR 和 OPND 分别为运算符栈和运算数栈, OP 为运算符集合。 InitStack (OPTR); Push (OPTR, #); initStack (OPND); c = getchar(); while (c!= # || GetTop(OPTR)!= #) { if (!In(c, OP)) { Push((OPND, c); c = getchar(); } // 不是运算符则进栈 else …… O P 为运算符集合,若 c 不 是运算符,则加入到运算 } // while 数栈中 return GetTop(OPND);} // EvaluateExpression
  48. 48. switch ( precede(GetTop(OPTR), c) { case <: // 栈顶元素优先权低 Push(OPTR, c); c = getchar(); break; case =: // 脱括号并接收下一字符 Pop(OPTR, x); c = getchar(); break; case > : // 退栈并将运算结果入栈 Pop(OPTR, theta); Pop(OPND, b); Pop(OPND, a); Push(OPND, Operate(a, theta, b)); break; } // switch
  49. 49. 算法演示( 3+5 ) * 2- 12 + 6 / 3 #
  50. 50. 第三章作业3.1 设将整数 1 、 2 、 3 、 4 依次进栈,但只要出栈时栈非空,则可将出栈操作按任何次序夹入其中,请回答下列问题: ( 1 )若入栈次序为push(1) , pop() , push(2 ), push(3) , pop(), pop( ) , push(4) , pop( ) ,则出栈的数字序列为什么?3.2 写出检验括号匹配的算法。
  51. 51. §3.4 队列3.4.1 队列的类型定义3.4.2 链队列3.4.3 循环队列
  52. 52. 3.4.1 队列的类型定义队列是限定只能在表的一端进行插入, 在表的另一端进行删除的线性表。出 a1 a2 a3…………………….an 入队队 front rear 队列 Q=(a1,a2,……,an) 队尾 (rear)—— 允许插入的一端 队头 (front)—— 允许删除的一端 队列特点:先进先出 ( F IF O )
  53. 53. 队列的类型定义ADT Queue { 数据对象: D = {ai | ai∈ ElemSet, i=1,2,...,n, n≥0} 数据关系: R1 = { <a i-1,ai > | ai-1, ai ∈ D, i=2,...,n} 约定其中 a1 端为队列头 , an 端为队列尾 基本操作: } ADT Queue
  54. 54. 队列的基本操作:InitQueue(&Q) DestroyQueue(&Q)QueueEmpty(Q) QueueLength(Q)GetHead(Q, &e) ClearQueue(&Q)EnQueue(&Q, e) DeQueue(&Q, &e)QueueTravers(Q, visit())
  55. 55. 3.4.2 链队列-队列的链式表示和 实现 typedef struct QNode{// 结点类型 QElemType data ; struct QNode *next ; }QNode, *QueuePtr; typedef struct{ // 链队列类型 QueuePtr front ; // 队头指针 QueuePtr rear ; // 队尾指针 } LinkQueue;
  56. 56. Q.front a1 … an ∧Q.rear Q.front 空队 ∧ Q.rear 列
  57. 57. 空 队 ^ Q.rear -> next=p front rear p Q.rear=px 入队 x ^ front rear py 入队 x y ^ front rear px 出队 x y ^ front reary 出队 ^ p= Q.front -> next Q.front -> next = p -> next front ear r
  58. 58. Status InitQueue (LinkQueue &Q) { // 构造一个空队列 Q Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode)); if (!Q.front) exit (OVERFLOW); // 存储分配失败 Q.front->next = NULL; return OK;}
  59. 59. Status EnQueue (LinkQueue &Q, QElemType e) { // 插入元素 e 为 Q 的新的队尾元素 p = (QueuePtr) malloc (sizeof (QNode)); if (!p) exit (OVERFLOW); // 存储分配失败 p->data = e; p->next = NULL; Q.rear->next = p; Q.rear = p; return OK;}
  60. 60. EnQueue p ... ∧ e ∧ Q.front Q.rear Q.rear p->data = e; p->next = NULL; Q.rear->next = p; Q.rear = p;
  61. 61. Status DeQueue (LinkQueue &Q, QElemType &e) { // 若队列不空,则删除 Q 的队头元素, // 用 e 返回其值,并返回 OK ;否则返回ERROR if (Q.front == Q.rear) return ERROR; p = Q.front->next; e = p->data; Q.front->next = p->next; if (Q.rear == p) Q.rear = Q.front; free (p); return OK;
  62. 62. DeQueue p e ... ∧ Q.front Q.rear p = Q.front->next; e = p->data; Q.front->next = p->next; if (Q.rear == p) Q.rear = Q.front; free (p);
  63. 63. 3.4.3 循环队列-队列的顺序表示和实现#define MAXQSIZE 100 // 最大队列长度typedef struct { QElemType *base; // 动态分配存储空间 int front; // 头指针,若队列不空, // 指向队列头元素 int rear; // 尾指针,若队列不空,指向 // 队列尾元素 的下一个位置} SqQueue;
  64. 64. 实现:用一维数组实现 sq[M] rear 5 5 5 J6 5 4 4 4 J5 4 rear rear J4 3 3 3 3 rear 2 J3 2 front J3 2 front 2 rear front J2 1 J2 1 front 1 1rear=0 rear 0 front J1 0 front J1 0 0front=0 J4,J5,J6 入 空队列 J1,J1,J3 入队 J1,J2,J3 出队 队存在问题:当 front=0,rear=M 时再有元素入队发生溢出 — — 真溢出当 front≠0,rear=M 时再有元素入队发生溢出 — — 假
  65. 65. 解决方案 1 队首固定,每次出队剩余元素向下移 动 , 避免发生假溢出 .Rear Rear J6 J6 J5 J5 Rear J6 J4 J4 J5 J3 J3 J4front J2 front front J3方法可行 , 但将花费太多的时间
  66. 66. 解决方案 2 循环队列  基本思想:把队列设想成环形,让 sq[0] 接在 sq[M-1] 之后,若 rear+1==M, 则令 rear=0;M-1 J6 Rear M-1 J6 ... ... ... ... 2 J5 2 J5 1 J4 Front 1 J4 Front 0 0 J7 Rear rear+1 = M, 于是令 rear = 0, “J7” 入队 , 有 sq[rear] = J7. 能够避免假溢出 .
  67. 67. 解决方案 2循环队列 实现: 利用 “ 模 ” 运算,实现若 rear+1==M, 则 rear=0 的设定 ; 入队: sq[rear]=x; rear=(rear+1)%M; 出队: x=sq[front]; front=(front+1)%M; 队满、队空判定条件
  68. 68. front 队空: fro nt==re a r rear 队满: fro nt==re a r 4 5 0 3 2 1 rear J6 J5 4 5 0 3 J4,J5 ,J6 出 队 J6 2 1 J4 J7,J8 ,J9 入 J5 4 5 0 J7front 队 3 2 1 J8 J4 初始状态 front J9 rear 队满队空如何区分判定 ?
  69. 69. 解决方案:1. 另外设一个标志位以区别队空、队满2. 少用一个元素空间: 队空: front==rear 队满:(rear+1)%M==front front rear J6 J5 4 5 0 J7 4 5 0 3 3 J4 2 1 J8 2 1 front rear3. 使用一个计数器记录队列中元素的总数当 count = Sq.length 时为队满 .
  70. 70. 分析可见 , 在 C 语言中不能用动态分配的一维数组来实现循环队列 . 如果用户的应用程序中设有循环队列 , 则必须为它设定一个最大队列长度 ; 若用户无法预估所用队列的长度 , 则宜用链队列 .
  71. 71. Status InitQueue (SqQueue &Q) { // 构造一个空队列 Q Q.base = (QElemType *) malloc (MAXQSIZE *sizeof (QElemType)); if (!Q.base) exit (OVERFLOW); // 存储分配失败 Q.front = Q.rear = 0; return OK;}
  72. 72. Status EnQueue (SqQueue &Q, QElemTypee) { // 插入元素 e 为 Q 的新的队尾元素 if ((Q.rear+1) % MAXQSIZE == Q.front) return ERROR; // 队列满 Q.base[Q.rear] = e; Q.rear = (Q.rear+1) % MAXQSIZE; return OK;}
  73. 73. Status DeQueue (SqQueue &Q, QElemType &e){ // 若队列不空,则删除 Q 的队头元素, // 用 e 返回其值,并返回 OK; 否则返回 ERROR if (Q.front == Q.rear) return ERROR; // 队空 e = Q.base[Q.front]; Q.front = (Q.front+1) % MAXQSIZE; return OK;}
  74. 74. 例 . 写出以下程序段的输出结果(队列中的元素 类型 QElemType 为 char )。Void main( ){ Queue Q; InitQueue(Q); Char x=‘e’, y=‘c’; EnQueue(Q, ‘h’); EnQueue(Q, ‘r’); EnQueue(Q, y); DeQueue(Q, x); EnQueue(Q, x); DeQueue(Q, x); EnQueue(Q, ‘a’); While ( !QueueEmpty(Q) ){ DeQueue(Q, y); Printf(y); } Printf(x);}
  75. 75. 括号匹配的检验1 )凡出现左括弧,则进栈;2 )凡出现右括弧,首先检查栈是否空 若栈空,则表明该“ 右括弧” 多余, 否则和栈顶元素比较, 若相匹配,则“ 左括弧出栈” , 否则表明不匹配。3 )表达式检验结束时, 若栈空,则表明表达式中匹配正确, 否则表明“ 左括弧” 有余。
  76. 76. 括号匹配的检验Status check( ) { // 对于输入的任意一个字符串 , 检验括号是否配对SqStack s;SElemType ch[80], *p,e; //c h 存放字符串InitStack(s); // 初始化栈成功Gets(ch); // 输入字符串p = ch; //p 指向字符串的首字符... // 输入字符串中括号的检验
  77. 77. 括号匹配的检验while(*p) // 没有到串尾Switch(*p){case ‘(’, ‘[’ , ‘{’ : Push(s,*p++); break; // 左括号入栈且 p++case ‘)’, ‘]’ , ‘}’ : if (!StackEmpty(s)) { // 若栈不空 Pop(s,e); // 弹出栈顶元素 if (!(e==‘(’&& *p ==‘)’ || e==‘[’&& *p ==‘]’|| e==‘{’&& *p ==‘} ’) // 出现了 3 种匹配情况之外的情况
  78. 78. 括号匹配的检验{ printf(“ 左右括号不匹配 n”); return ERROR;}p++;}else {// 栈空 printf( “ 缺乏左括号 n”); return ERROR;} //end ifdefault: p++; // 其它字符不处理,指针向后移} //end switch
  79. 79. 括号匹配的检验if (StackEmpty(s)) // 字符串结束时栈空 printf(“ 括号匹配 n”);else printf(“ 缺少右括号 n”);}//end check

×