SlideShare a Scribd company logo
1 of 79
通常称,栈和队列是限定插入和
删除 只能在表的 “ 端点 ” 进行的线性表。

  线性表                  栈               队列
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

栈和队列是两种常用的数据类型
第三章 栈和队列
 3.1 栈 (stack)
 3.2 栈的应用举例
 3.4 队列
 (Queue)
学习提要:
1. 掌握栈和队列这两种抽象数据类型的特
点,
    并能在相应的应用问题中正确选用它们。
2. 熟练掌握栈类型的两种实现方法,即两
种存
    储结构表示时的基本操作实现算法,特别
应
 注意栈满和栈空的条件以及它们的描述方
法。
3. 熟练掌握循环队列和链队列的基本操作
实现
§3.1 栈( stack )
3.1.1 栈的类型定义

3.1.2 栈的表示和实现
3.1.1 栈的类型定义
 栈的定义和特点
  定义:限定仅在表尾进行插入或删
   除操作的线性表,表尾 — 栈顶,表头
   — 栈底,不含元素的空表称空栈。
     进栈           出栈
    栈     ...
          an
    顶
          ……...


                   栈 s=(a1,a2,……,an)
          a2
   栈底     a1

特点:先进后出( FILO )或后进先
出( LIFO )
 栈的类型定义
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
InitStack(&S)
                 DestroyStack(&S)
StackLength(S)
                 StackEmpty(s)
GetTop(S, &e)
                 ClearStack(&S)
Push(&S, e)
                 Pop(&S, &e)
StackTravers(S, visit())
3.1.2 栈的表示和实现
    顺序栈
          类似于线性表的顺序映象实
      现,指向表尾的指针可以作为栈
      顶指针。
//----- 栈的顺序存储表示 -----
#define STACK_INIT_SIZE 100; // 存储空间初始分配量
#define STACKINCREMENT 10;// 存储空间分配增量
typedef struct {
  SElemType *base; // 栈底指针
  SElemType *top; // 栈顶指针
  int stacksize; // 栈的当前可使用的最大容量
} SqStack;
实现:一维数组 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   1
top                               top   A    0
base        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)
Status InitStack (SqStack &S)
{// 构造一个空栈 S
S.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;
}
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;
Status Pop (SqStack &S, SElemType &e) {
   // 若栈不空,则删除 S 的栈顶元素,
   // 用 e 返回其值,并返回 OK ;
   // 否则返回 ERROR
  if (S.top == S.base) return ERROR;
  e = *--S.top;
  return OK;
}
链栈
    栈的链式存储结构。栈顶指针
 就是链表的头指针。
栈顶指针
       an   an-1   a1 ∧


 注意 : 链栈
 中指针的方
 向
 入栈操
       作
       top top
                                 栈底
p      x                  …...    ^


    p->next=top ; top=p
       出栈操
       作
       q  top
                                 栈底
top                       …...    ^

q=top ; top=top->next; //q 返回了出栈的元素
§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                       出
             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
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
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          l    l   i   i
      h          h    h   h   h
      w          w    w   w   w
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
Void LineEdit( ){
// 利用字符栈 S, 从终端接收一行并传送至调用过程的数据
   区
  InitStack(S); // 构造空栈
  ch = getchar(); // 从终端接受一个字符
  While(ch!= EOF){ //EOF 为全文结束符
  ... // 对输入的字符的判断与处理
ClearStack(S);     // 重置 S 为空栈
if (ch != EOF) ch = getchar();}
DestroyStack(S);
} //LineEdid
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(); // 从终端接收下一个字符
 }
将从栈底到栈顶的字符传送至调用过程的
数据区;
3.2.4 迷宫求解
通常用的是“ 穷举求解 ” 的方法
   #   # # # # # # # #   #
   #   →↓ # $ $ $ #      #
   #     ↓ # $ $ $ #     #
   #   ↓ ←$ $ # #        #
   #   ↓ # # #       #   #
   #   →→↓ #         #   #
   #     # →→↓ #         #
   #   # # # # ↓ # #     #
   #           →→→Θ      #
   #   # # # # # # # #   #
求迷宫路径算法的基本思想是
:
若当前位置“ 可通 ” ,则纳入
 路径,继续前进 ;
若当前位置“ 不可通 ” ,则后
 退,换方向继续探索 ;
若四周“ 均无通路 ” ,则将当前
 位 置从路径中删除出去。
“ 下一个位置 ” 指 “ 当前位置 ” 四周 “
 东南西北 ” 四个方向上相临的通道块
 。

         北

     西       东

         南
             当前位置
根据算法思想 , 为了保证在任何
位置上都能沿原路退回 , 需要用一个后
进先出的结构来保存从入口到当前位置
的路径 .
     假设以栈 S 记录 “ 当前路径 ” ,则
栈顶中存放的是 “ 当前路径上最后一个
通道块 ” 。由此,“ 纳入路径 ” 的操作即
为 “ 当前位置入栈 ” ;“ 从当前路径上删
除前一通道块 ” 的操作为 “ 出栈 ” 。
算法描述
设定当前位置的初值为入口位置
do {
     若当前位置可通
     则 { 将当前位置插入栈顶 ; // 纳入路径
        若该位置是出口位置 , 则结束 ; // 求得路径存
 放在栈中
        否则切换当前位置的东相邻方块为新的当前
 位置 ;
    }
                出口         东

   当前位置       当前位置          当前位置
算法描述
否则  // 不通
若栈不空且栈顶位置尚有其他方向未探索 ,
  则设定新的当前位置为沿顺时针方向旋转找
 的栈顶位置的下一相邻块 ;




       栈顶位       当前位
        置         置
算法描述
若栈不空但栈顶位置的四周均不可通
则 { 删去栈顶位置 ;
         若栈不空 , 则重新测试新的栈顶位
      置,
         直到找到一个可通的相邻块或出栈
      至栈空 ;
   }
}while( 栈不空 );
                    当前位
                     置
算法描述
说明 : 当前位置可通 , 指未曾走到过的通道
块 , 即要求该通道块既不在当前路径上 , 也
不是曾经纳入过路径的通道块 .

     a
起点                    a-b-c-f-g ( 简单路径 )
                 b
d        c             a-b-c-d-e-c-f-g ( 非简单路径
                     f ) 若 b 已经纳入过路径 , 则在当
e
                         前位置为 f 时 , 将不作为可通
    终点       g           位置考虑 , 否则 , 将形成死循
                         环.
通道块的数据描述
Type struct {
int ord; // 通道块在路径上的 “ 序号 ”
PosType seat; // 通道块在迷宫中的 “ 坐标
 ”
int   di;   // 从此通道块走向下一通道块的 “ 方
 向”
} SElemType;   // 栈的元素类型
迷宫问题算法
Status MazePath(MazeType maze,PosType start, PosType
end){
// 若迷宫 maze 中存在从 start 到 end 的通道,则求得一
条存放在栈中,并返回 true ;否则返回 false
InitStack(S); curpos = start; // 设定当前位置为 “ 入口位置
”
curstep = 1; // 探索第一步
do {
        ...
    } while (!StackEmpty(s));
return (false);
} //MazePath
迷宫问题算法
do {
       if (Pass(curpos)) { // 当前位置可通
            FootPrint(curpos); // 留下足迹
            e = ( curstep,curpos,1);
            Push(S,e); // 入栈,加入足迹
            if (curpos == end) return (ture); // 到达出口
            curpos = NextPos(curpos,1);
                                // 下一个位置为当前位置的东邻
          curstep++; // 探索下一个位置
        }// if
        ...
迷宫问题算法
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
迷宫问题演示




  成功     不成功
3.2.5 表达式求值
限于二元运算符的表达式定义 :

 Exp = S1 OP S2
操作数 : 变量、常量、表达式
运算符 : 算术运算符、关系运算符、
          逻辑运算符
界限符:括号、结束符
算法思想:
      设置两个工作栈, OPTR 存运算符
, OPND 存操作数及运算结果。算法运算
思想:
1. 首先置 OPND 栈为空, “ #” 为运算符
栈底元素。

2. 依次读入表达式中的每个字符,若是
操作数进 OPND 栈 , 若是运算符则和
OPTR 栈顶元素比较优先权后作相应操
作,直至整个表达式求值完毕。
例: 3 * ( 7 – 2 ) #
    C C C C C C C   C




                          -

          2               (      7-2
          5
          7               *
                                 3*5
          15
          3               #


        OPND 栈          OPTR 栈
例: 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
例: 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” 和
例: 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 )

“)” =“(” , 操作符的优先权相等,弹出“ (”,
脱括号并接收下一字符 .
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
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
算法演示
( 3+5 ) * 2- 12 + 6 / 3 #
第三章作业
3.1 设将整数 1 、 2 、 3 、 4 依次进栈,但只要
出栈时栈非空,则可将出栈操作按任何次序夹
入其中,请回答下列问题:
     ( 1 )若入栈次序为
push(1) , pop() , push(2 ), push(3) , pop()
, pop( ) , push(4) , pop( ) ,则出栈的数字序
列为什么?
3.2 写出检验括号匹配的算法。
§3.4 队列
3.4.1 队列的类型定义
3.4.2 链队列
3.4.3 循环队列
3.4.1 队列的类型定义
队列是限定只能在表的一端进行插入,
 在表的另一端进行删除的线性表。
出
      a1     a2   a3…………………….an      入队
队
     front                    rear
             队列 Q=(a1,a2,……,an)
    队尾 (rear)—— 允许插入的一端
    队头 (front)—— 允许删除的一端
    队列特点:先进先出 ( F IF O )
队列的类型定义
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
队列的基本操作:
InitQueue(&Q)     DestroyQueue(&Q)
QueueEmpty(Q)     QueueLength(Q)
GetHead(Q, &e) ClearQueue(&Q)
EnQueue(&Q, e) DeQueue(&Q, &e)
QueueTravers(Q, visit())
3.4.2 链队列-队列的链式表示和
                实现
   typedef struct QNode{// 结点类型
   QElemType     data ;
   struct QNode *next ;
  }QNode, *QueuePtr;
 typedef struct{ // 链队列类型
   QueuePtr front ; // 队头指针
   QueuePtr rear ; // 队尾指针
 } LinkQueue;
Q.front             a1       …   an   ∧

Q.rear

          Q.front
  空队                     ∧
          Q.rear
  列
空
 队               ^               Q.rear -> next=p
        front rear       p          Q.rear=p
x 入队                 x       ^
    front            rear                 p
y 入队                  x               y       ^
       front                          rear
                         p
x 出队                 x            y       ^
     front                     rear
y 出队         ^           p= Q.front -> next
                     Q.front -> next = p -> next
     front ear
         r
Status InitQueue (LinkQueue &Q) {
    // 构造一个空队列 Q
    Q.front = Q.rear =
             (QueuePtr)malloc(sizeof(QNode));
    if (!Q.front) exit (OVERFLOW);
                         // 存储分配失败
    Q.front->next = NULL;
    return OK;
}
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;
}
EnQueue                          p
            ...      ∧      e        ∧


  Q.front         Q.rear    Q.rear


 p->data = e; p->next = NULL;
 Q.rear->next = p; Q.rear = p;
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;
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);
3.4.3 循环队列-队列的顺序表示和实
现
#define MAXQSIZE 100 // 最大队列长度
typedef struct {
  QElemType *base; // 动态分配存储空间
  int front; // 头指针,若队列不空,
            // 指向队列头元素
  int rear;     // 尾指针,若队列不空,指向
               // 队列尾元素 的下一个位置
} SqQueue;
实现:用一维数组实现 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                       1
rear=0         rear
           0   front   J1   0 front   J1   0                       0
front=0                                               J4,J5,J6 入
        空队列    J1,J1,J3 入队 J1,J2,J3 出队                    队

存在问题:
当 front=0,rear=M 时再有元素入队发生溢出 — — 真
溢出
当 front≠0,rear=M 时再有元素入队发生溢出 — — 假
解决方案 1
  队首固定,每次出队剩余元素向下移
  动 , 避免发生假溢出 .
Rear         Rear
        J6           J6
        J5           J5   Rear    J6
        J4           J4           J5
        J3           J3           J4
front   J2   front        front   J3


方法可行 , 但将花费太多的时间
解决方案 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. 能够避免假溢出 .
解决方案 2
循环队列
 实现: 利用 “ 模 ” 运算,实现若 rear+1==M,
 则 rear=0 的设定 ;

             入队:        sq[rear]=x;
              rear=(rear+1)%M;
             出队: x=sq[front];
              front=(front+1)%M;
             队满、队空判定条件
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 J7
front
                               队          3 2 1 J8
                                       J4
        初始状态                front          J9
                                rear

             队满队空如何区分判定 ?
解决方案:
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
                                    rear
3. 使用一个计数器记录队列中元素的总数
当 count = Sq.length 时为队满 .
分析可见 , 在 C 语言中不能用
动态分配的一维数组来实现循环队
列 . 如果用户的应用程序中设有循环
队列 , 则必须为它设定一个最大队列
长度 ; 若用户无法预估所用队列的
长度 , 则宜用链队列 .
Status InitQueue (SqQueue &Q) {
  // 构造一个空队列 Q
  Q.base = (QElemType *) malloc
        (MAXQSIZE *sizeof (QElemType));
   if (!Q.base) exit (OVERFLOW);
                          // 存储分配失败
   Q.front = Q.rear = 0;
    return OK;
}
Status EnQueue (SqQueue &Q, QElemType
e) { // 插入元素 e 为 Q 的新的队尾元素
  if ((Q.rear+1) % MAXQSIZE == Q.front)
      return ERROR; // 队列满
  Q.base[Q.rear] = e;
   Q.rear = (Q.rear+1) % MAXQSIZE;
   return OK;
}
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;
}
例 . 写出以下程序段的输出结果(队列中的
元素
         类型 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);
}
括号匹配的检验
1 )凡出现左括弧,则进栈;
2 )凡出现右括弧,首先检查栈是否空
   若栈空,则表明该“ 右括弧” 多余,
   否则和栈顶元素比较,
   若相匹配,则“ 左括弧出栈” ,
   否则表明不匹配。
3 )表达式检验结束时,
   若栈空,则表明表达式中匹配正确
,
   否则表明“ 左括弧” 有余。
括号匹配的检验
Status check( ) { // 对于输入的任意一个字符串 , 检
验括号是否配对
SqStack s;
SElemType ch[80], *p,e; //c h 存放字符串

InitStack(s); // 初始化栈成功
Gets(ch); // 输入字符串
p = ch; //p 指向字符串的首字符

... // 输入字符串中括号的检验
括号匹配的检验
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 种匹配情况之外的情况
括号匹配的检验
{ printf(“ 左右括号不匹配 n”);
    return ERROR;
}
p++;
}
else {// 栈空
       printf( “ 缺乏左括号 n”);
       return ERROR;
} //end if
default: p++; // 其它字符不处理,指针向后移
} //end switch
括号匹配的检验
if (StackEmpty(s)) // 字符串结束时栈空
  printf(“ 括号匹配 n”);
else printf(“ 缺少右括号 n”);
}//end check

More Related Content

What's hot

Scala function-and-closures
Scala function-and-closuresScala function-and-closures
Scala function-and-closureswang hongjiang
 
数据结构复习笔记 图的邻接表存储表示及重要的基本操作
数据结构复习笔记 图的邻接表存储表示及重要的基本操作数据结构复习笔记 图的邻接表存储表示及重要的基本操作
数据结构复习笔记 图的邻接表存储表示及重要的基本操作mengyingchina
 
6, awk
6, awk6, awk
6, awkted-xu
 
C語言 第4章 基本輸出與輸入功能
C語言 第4章 基本輸出與輸入功能C語言 第4章 基本輸出與輸入功能
C語言 第4章 基本輸出與輸入功能shademoon
 
10 檔案說明與處理
10 檔案說明與處理10 檔案說明與處理
10 檔案說明與處理shademoon
 
1 C入門教學
1  C入門教學1  C入門教學
1 C入門教學Sita Liu
 
第4章 自顶向下的语法分析
第4章 自顶向下的语法分析第4章 自顶向下的语法分析
第4章 自顶向下的语法分析tjpucompiler
 
Pl 06 p10.prn
Pl 06 p10.prnPl 06 p10.prn
Pl 06 p10.prnjokertest
 
C程式-陣列與指標
C程式-陣列與指標C程式-陣列與指標
C程式-陣列與指標艾鍗科技
 
C程式-函式與巨集
C程式-函式與巨集C程式-函式與巨集
C程式-函式與巨集艾鍗科技
 
第5章 自底向上的语法分析
第5章 自底向上的语法分析第5章 自底向上的语法分析
第5章 自底向上的语法分析tjpucompiler
 
竞赛中C++语言拾遗
竞赛中C++语言拾遗竞赛中C++语言拾遗
竞赛中C++语言拾遗乐群 陈
 
Lua 语言介绍
Lua 语言介绍Lua 语言介绍
Lua 语言介绍gowell
 
Intro to C++ Basic
Intro to C++ BasicIntro to C++ Basic
Intro to C++ BasicShih Chi Lin
 
07 陣列與字串
07 陣列與字串07 陣列與字串
07 陣列與字串shademoon
 

What's hot (20)

functional-scala
functional-scalafunctional-scala
functional-scala
 
Scala function-and-closures
Scala function-and-closuresScala function-and-closures
Scala function-and-closures
 
数据结构复习笔记 图的邻接表存储表示及重要的基本操作
数据结构复习笔记 图的邻接表存储表示及重要的基本操作数据结构复习笔记 图的邻接表存储表示及重要的基本操作
数据结构复习笔记 图的邻接表存储表示及重要的基本操作
 
6, awk
6, awk6, awk
6, awk
 
C語言 第4章 基本輸出與輸入功能
C語言 第4章 基本輸出與輸入功能C語言 第4章 基本輸出與輸入功能
C語言 第4章 基本輸出與輸入功能
 
10 檔案說明與處理
10 檔案說明與處理10 檔案說明與處理
10 檔案說明與處理
 
1 C入門教學
1  C入門教學1  C入門教學
1 C入門教學
 
第4章 自顶向下的语法分析
第4章 自顶向下的语法分析第4章 自顶向下的语法分析
第4章 自顶向下的语法分析
 
Pl 06 p10.prn
Pl 06 p10.prnPl 06 p10.prn
Pl 06 p10.prn
 
Ch 8
Ch 8Ch 8
Ch 8
 
C程式-陣列與指標
C程式-陣列與指標C程式-陣列與指標
C程式-陣列與指標
 
C程式-函式與巨集
C程式-函式與巨集C程式-函式與巨集
C程式-函式與巨集
 
第5章 自底向上的语法分析
第5章 自底向上的语法分析第5章 自底向上的语法分析
第5章 自底向上的语法分析
 
Ch9
Ch9Ch9
Ch9
 
竞赛中C++语言拾遗
竞赛中C++语言拾遗竞赛中C++语言拾遗
竞赛中C++语言拾遗
 
Lua 语言介绍
Lua 语言介绍Lua 语言介绍
Lua 语言介绍
 
系統程式 - 附錄
系統程式 - 附錄系統程式 - 附錄
系統程式 - 附錄
 
第三章
第三章第三章
第三章
 
Intro to C++ Basic
Intro to C++ BasicIntro to C++ Basic
Intro to C++ Basic
 
07 陣列與字串
07 陣列與字串07 陣列與字串
07 陣列與字串
 

Viewers also liked

第六章 树和二叉树2
第六章 树和二叉树2第六章 树和二叉树2
第六章 树和二叉树2Wang Yizhe
 
Ramon pascual
Ramon pascualRamon pascual
Ramon pascualjordivil
 
MReady @ Programatica - AGORA 2012
MReady @ Programatica - AGORA 2012MReady @ Programatica - AGORA 2012
MReady @ Programatica - AGORA 2012mReady
 
Mekanisme verifikasi registrasi_ulang-1
Mekanisme verifikasi registrasi_ulang-1Mekanisme verifikasi registrasi_ulang-1
Mekanisme verifikasi registrasi_ulang-1Idicab Jaksel
 
Acta constitucion
Acta constitucionActa constitucion
Acta constitucionlancord2000
 
Passeio Cicloturístico à Gralheira
Passeio Cicloturístico à GralheiraPasseio Cicloturístico à Gralheira
Passeio Cicloturístico à GralheiraO Ciclista
 
Clase 1 c9 ago_12_2013
Clase 1 c9 ago_12_2013Clase 1 c9 ago_12_2013
Clase 1 c9 ago_12_2013Luis Stolz
 
Prezentare - A.O Parlamentul European al Tinerilor
Prezentare - A.O Parlamentul European al TinerilorPrezentare - A.O Parlamentul European al Tinerilor
Prezentare - A.O Parlamentul European al TinerilorAlexandr Taran
 
2011 08-14 - mudar o mundo
2011 08-14 - mudar o mundo2011 08-14 - mudar o mundo
2011 08-14 - mudar o mundoO Ciclista
 
Pio.la cooperacion ciudadana para e ldesarrollo
Pio.la cooperacion ciudadana para e ldesarrolloPio.la cooperacion ciudadana para e ldesarrollo
Pio.la cooperacion ciudadana para e ldesarrolloRosa Cristina Parra Lozano
 

Viewers also liked (20)

第六章 树和二叉树2
第六章 树和二叉树2第六章 树和二叉树2
第六章 树和二叉树2
 
Ap ch01
Ap ch01Ap ch01
Ap ch01
 
Ap ch10
Ap ch10Ap ch10
Ap ch10
 
Ap ch09
Ap ch09Ap ch09
Ap ch09
 
30313664 assignment1
30313664 assignment130313664 assignment1
30313664 assignment1
 
Ap ch14
Ap ch14Ap ch14
Ap ch14
 
Ramon pascual
Ramon pascualRamon pascual
Ramon pascual
 
Ap ch05
Ap ch05Ap ch05
Ap ch05
 
Character
CharacterCharacter
Character
 
MReady @ Programatica - AGORA 2012
MReady @ Programatica - AGORA 2012MReady @ Programatica - AGORA 2012
MReady @ Programatica - AGORA 2012
 
Maindboar
MaindboarMaindboar
Maindboar
 
Mekanisme verifikasi registrasi_ulang-1
Mekanisme verifikasi registrasi_ulang-1Mekanisme verifikasi registrasi_ulang-1
Mekanisme verifikasi registrasi_ulang-1
 
Acta constitucion
Acta constitucionActa constitucion
Acta constitucion
 
Passeio Cicloturístico à Gralheira
Passeio Cicloturístico à GralheiraPasseio Cicloturístico à Gralheira
Passeio Cicloturístico à Gralheira
 
Texto alvarez
Texto alvarezTexto alvarez
Texto alvarez
 
Frases ilustradas
Frases ilustradasFrases ilustradas
Frases ilustradas
 
Clase 1 c9 ago_12_2013
Clase 1 c9 ago_12_2013Clase 1 c9 ago_12_2013
Clase 1 c9 ago_12_2013
 
Prezentare - A.O Parlamentul European al Tinerilor
Prezentare - A.O Parlamentul European al TinerilorPrezentare - A.O Parlamentul European al Tinerilor
Prezentare - A.O Parlamentul European al Tinerilor
 
2011 08-14 - mudar o mundo
2011 08-14 - mudar o mundo2011 08-14 - mudar o mundo
2011 08-14 - mudar o mundo
 
Pio.la cooperacion ciudadana para e ldesarrollo
Pio.la cooperacion ciudadana para e ldesarrolloPio.la cooperacion ciudadana para e ldesarrollo
Pio.la cooperacion ciudadana para e ldesarrollo
 

Similar to 第三章 栈和队列(新)

JCConf 2023 - 深入淺出 Java 21 功能
JCConf 2023 - 深入淺出 Java 21 功能JCConf 2023 - 深入淺出 Java 21 功能
JCConf 2023 - 深入淺出 Java 21 功能Joseph Kuo
 
数据结构复习笔记 静态链表
数据结构复习笔记 静态链表数据结构复习笔记 静态链表
数据结构复习笔记 静态链表mengyingchina
 
搜索初步
搜索初步搜索初步
搜索初步AXM
 
博大正方C语言试题V2.0
博大正方C语言试题V2.0博大正方C语言试题V2.0
博大正方C语言试题V2.0yiditushe
 
第3章 词法分析
第3章 词法分析第3章 词法分析
第3章 词法分析tjpucompiler
 
第6章 自底向上的lr分析法
第6章 自底向上的lr分析法第6章 自底向上的lr分析法
第6章 自底向上的lr分析法tjpucompiler
 
自然语言处理 中文分词程序实验报告%28含源代码%29
自然语言处理 中文分词程序实验报告%28含源代码%29自然语言处理 中文分词程序实验报告%28含源代码%29
自然语言处理 中文分词程序实验报告%28含源代码%29aemoe
 
数据结构复习笔记 关键路径+Critical+paths
数据结构复习笔记 关键路径+Critical+paths数据结构复习笔记 关键路径+Critical+paths
数据结构复习笔记 关键路径+Critical+pathsmengyingchina
 
人机对弈编程概述
人机对弈编程概述人机对弈编程概述
人机对弈编程概述勇浩 赖
 
Ihome inaction 篇外篇之fp介绍
Ihome inaction 篇外篇之fp介绍Ihome inaction 篇外篇之fp介绍
Ihome inaction 篇外篇之fp介绍dennis zhuang
 
Bash shell script 教學
Bash shell script 教學Bash shell script 教學
Bash shell script 教學Ming-Sian Lin
 
Arrays的Sort算法分析
Arrays的Sort算法分析Arrays的Sort算法分析
Arrays的Sort算法分析Zianed Hou
 
第七章 图[3]
第七章 图[3]第七章 图[3]
第七章 图[3]Wang Yizhe
 
程式設計師的自我修養 Chapter 10 記憶體
程式設計師的自我修養 Chapter 10 記憶體程式設計師的自我修養 Chapter 10 記憶體
程式設計師的自我修養 Chapter 10 記憶體Shu-Yu Fu
 
Ruby程式語言入門導覽
Ruby程式語言入門導覽Ruby程式語言入門導覽
Ruby程式語言入門導覽Mu-Fan Teng
 
221013 GDSC Kotlin Basics.pptx
221013 GDSC Kotlin Basics.pptx221013 GDSC Kotlin Basics.pptx
221013 GDSC Kotlin Basics.pptxNCUDSC
 
Python learn guide
Python learn guidePython learn guide
Python learn guiderobin yang
 

Similar to 第三章 栈和队列(新) (20)

JCConf 2023 - 深入淺出 Java 21 功能
JCConf 2023 - 深入淺出 Java 21 功能JCConf 2023 - 深入淺出 Java 21 功能
JCConf 2023 - 深入淺出 Java 21 功能
 
数据结构复习笔记 静态链表
数据结构复习笔记 静态链表数据结构复习笔记 静态链表
数据结构复习笔记 静态链表
 
搜索初步
搜索初步搜索初步
搜索初步
 
博大正方C语言试题V2.0
博大正方C语言试题V2.0博大正方C语言试题V2.0
博大正方C语言试题V2.0
 
第3章 词法分析
第3章 词法分析第3章 词法分析
第3章 词法分析
 
第6章 自底向上的lr分析法
第6章 自底向上的lr分析法第6章 自底向上的lr分析法
第6章 自底向上的lr分析法
 
第四章 串
第四章 串第四章 串
第四章 串
 
自然语言处理 中文分词程序实验报告%28含源代码%29
自然语言处理 中文分词程序实验报告%28含源代码%29自然语言处理 中文分词程序实验报告%28含源代码%29
自然语言处理 中文分词程序实验报告%28含源代码%29
 
数据结构复习笔记 关键路径+Critical+paths
数据结构复习笔记 关键路径+Critical+paths数据结构复习笔记 关键路径+Critical+paths
数据结构复习笔记 关键路径+Critical+paths
 
人机对弈编程概述
人机对弈编程概述人机对弈编程概述
人机对弈编程概述
 
Ihome inaction 篇外篇之fp介绍
Ihome inaction 篇外篇之fp介绍Ihome inaction 篇外篇之fp介绍
Ihome inaction 篇外篇之fp介绍
 
Maze Game
Maze GameMaze Game
Maze Game
 
Bash shell script 教學
Bash shell script 教學Bash shell script 教學
Bash shell script 教學
 
Arduino程式快速入門
Arduino程式快速入門Arduino程式快速入門
Arduino程式快速入門
 
Arrays的Sort算法分析
Arrays的Sort算法分析Arrays的Sort算法分析
Arrays的Sort算法分析
 
第七章 图[3]
第七章 图[3]第七章 图[3]
第七章 图[3]
 
程式設計師的自我修養 Chapter 10 記憶體
程式設計師的自我修養 Chapter 10 記憶體程式設計師的自我修養 Chapter 10 記憶體
程式設計師的自我修養 Chapter 10 記憶體
 
Ruby程式語言入門導覽
Ruby程式語言入門導覽Ruby程式語言入門導覽
Ruby程式語言入門導覽
 
221013 GDSC Kotlin Basics.pptx
221013 GDSC Kotlin Basics.pptx221013 GDSC Kotlin Basics.pptx
221013 GDSC Kotlin Basics.pptx
 
Python learn guide
Python learn guidePython learn guide
Python learn guide
 

More from Wang Yizhe

第九章+查找[4]
第九章+查找[4]第九章+查找[4]
第九章+查找[4]Wang Yizhe
 
第九章 查找[3]
第九章 查找[3]第九章 查找[3]
第九章 查找[3]Wang Yizhe
 
第九章 查找[2]
第九章 查找[2]第九章 查找[2]
第九章 查找[2]Wang Yizhe
 
第九章 查找[1]
第九章 查找[1]第九章 查找[1]
第九章 查找[1]Wang Yizhe
 
第七章 图[4]1
第七章 图[4]1第七章 图[4]1
第七章 图[4]1Wang Yizhe
 
第七章 图[2]1
第七章 图[2]1第七章 图[2]1
第七章 图[2]1Wang Yizhe
 
第七章 图[1]
第七章 图[1]第七章 图[1]
第七章 图[1]Wang Yizhe
 
数据结构 总结与复习
数据结构 总结与复习数据结构 总结与复习
数据结构 总结与复习Wang Yizhe
 
第四章 串操作应用举例
第四章 串操作应用举例第四章 串操作应用举例
第四章 串操作应用举例Wang Yizhe
 
第二章 线性表
第二章 线性表第二章 线性表
第二章 线性表Wang Yizhe
 

More from Wang Yizhe (11)

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