1、3 栈与队列,栈与队列,2/132,主要内容,栈 栈的应用 队列 队列的应用,栈与队列,3/132,栈与队列,栈和队列是在程序设计中被广泛使用的两种线性数据结构。 与线性表相比,它们的插入和删除操作受更多的约束和限定,故又称为限定性的线性表结构。,栈与队列,4/132,栈,栈:限定仅只能在末端进行插入和删除的线性表。 栈顶:允许插入和删除的一端。 栈底:不允许插入和删除的一端。 时间有序表:先进后出(FILO) /后进先出(LIFO),栈与队列,5/132,栈的基本操作, clear()清空栈。 isEmpty()判断栈是否为空。 push(el)将元素el放到栈的顶部。 pop()取出栈顶部
2、的元素。 topEl()获取栈顶部的元素,但不删除该元素。,栈与队列,6/132,栈顶指针top,指向实际栈顶 后的空位置,初值为0,进栈,A,出栈,栈满,B,C,D,E,F,设数组大小为M top=0,栈空,此时出栈,则下溢(underflow) top=M,栈满,此时入栈,则上溢(overflow),栈空,栈与队列,7/132,栈的表示和实现,顺序方式 链式方式,栈与队列,8/132,顺序表示的栈的实现,top,空栈: top = -1 MaxTop = = 4,3,1,2,0,栈与队列,9/132,栈的初始化操作,0 1 2 MaxTop-1,top,template Stack:Sta
3、ck(int MaxStackSize)MaxTop=MaxStackSize-1;stack=new TMaxStackSize;top=-1; ,栈与队列,10/132,进栈操作,0 1 2 maxSize-1,top,b,a,0 1 2 maxSize-1,top,b,a,c,template Stack ,栈与队列,11/132,出栈操作,template Stack ,0 1 2 maxSize-1,top,b,a,c,栈与队列,12/132,两个栈共享栈空间,-,-,0,MaxTop-1,b0,t0,t1,b1,该空间的两端分别设为两个栈的栈底,用b0(=-1)和b1(=MaxTo
4、p)指示。让两个栈的栈顶t0和t1都向中间伸展,直到两个栈的栈顶相遇,才认为发生了溢出。,栈与队列,13/132,链式栈,data link,栈顶,栈底,top,top,初始化,栈与队列,14/132,链式表示的栈的实现,data link,栈顶,栈底,top,top,初始化,template class Node friend class LinkedStack ; private: T data;Node * link; ,栈与队列,15/132,链式栈的进栈操作,template LinkedStack ,data link,栈与队列,16/132,data link,top,x = A
5、,template LinkedStack ,链式栈的出栈操作,栈与队列,17/132,链式栈的清空操作,template void LinkedStack:MakeEmpty() /同析构函数Node *next;while(top)next=top-link;delete top;top=next; ,栈与队列,18/132,栈的应用,数制转换 括号匹配 回文游戏 表达式计算 计算中缀表带式的值 中缀、后缀表达式转换 匹配程序分隔符 大数加法(乘法) 递归,栈与队列,19/132,将一个非负十进制整数转换成八进制数并输出。 例 把十进制数159转换成八进制数。,数制转换,栈与队列,20/1
6、32,括号匹配,匹配一个字符串中的左、右括号。如( a * ( b + c ) + d ),字符位置 1 2 3 4 5 6 7 8 9 10 11,匹配情况:左括号4 右括号8左括号1 右括号11,算法:从左向右扫描字符串, (1)如果是左括号,就把它的位置编码压入栈中 (2)如果是右括号,则与栈顶的位置编码所指的左括号匹配。,栈与队列,21/132,括号匹配,匹配一个字符串中的左、右括号。如( a * ( b + c ) + d ),字符位置 1 2 3 4 5 6 7 8 9 10 11,1,栈与队列,22/132,括号匹配,匹配一个字符串中的左、右括号。如( a * ( b + c )
7、 + d ),字符位置 1 2 3 4 5 6 7 8 9 10 11,1,栈与队列,23/132,括号匹配,匹配一个字符串中的左、右括号。如( a * ( b + c ) + d ),字符位置 1 2 3 4 5 6 7 8 9 10 11,1,栈与队列,24/132,括号匹配,匹配一个字符串中的左、右括号。如( a * ( b + c ) + d ),字符位置 1 2 3 4 5 6 7 8 9 10 11,1,4,栈与队列,25/132,括号匹配,匹配一个字符串中的左、右括号。如( a * ( b + c ) + d ),字符位置 1 2 3 4 5 6 7 8 9 10 11,1,4,
8、栈与队列,26/132,括号匹配,匹配一个字符串中的左、右括号。如( a * ( b + c ) + d ),字符位置 1 2 3 4 5 6 7 8 9 10 11,1,4,栈与队列,27/132,括号匹配,匹配一个字符串中的左、右括号。如( a * ( b + c ) + d ),字符位置 1 2 3 4 5 6 7 8 9 10 11,1,4,栈与队列,28/132,括号匹配,匹配一个字符串中的左、右括号。如( a * ( b + c ) + d ),字符位置 1 2 3 4 5 6 7 8 9 10 11,1,4,8 4 匹配,栈与队列,29/132,括号匹配,匹配一个字符串中的左、右
9、括号。如( a * ( b + c ) + d ),字符位置 1 2 3 4 5 6 7 8 9 10 11,1,8 4 匹配,栈与队列,30/132,括号匹配,匹配一个字符串中的左、右括号。如( a * ( b + c ) + d ),字符位置 1 2 3 4 5 6 7 8 9 10 11,1,8 4 匹配,栈与队列,31/132,括号匹配,匹配一个字符串中的左、右括号。如( a * ( b + c ) + d ),字符位置 1 2 3 4 5 6 7 8 9 10 11,1,8 4 匹配,栈与队列,32/132,括号匹配,匹配一个字符串中的左、右括号。如( a * ( b + c ) +
10、 d ),字符位置 1 2 3 4 5 6 7 8 9 10 11,1,8 4 匹配,11 1 匹配,栈与队列,33/132,括号匹配,匹配一个字符串中的左、右括号。如( a * ( b + c ) + d ),字符位置 1 2 3 4 5 6 7 8 9 10 11,8 4 匹配,11 1 匹配,栈与队列,34/132,算法思想: 读入字符串; 去掉空格(原串); 压入栈; 原串字符与出栈字符依次比较;若不等,非回文;若直到栈空都相等,回文。,回文游戏(顺读与逆读字符串一样(不含空格),栈与队列,35/132,表达式计算,表达式都由操作数(也称运算对象)、操作符(也成运算符)和分界符组成。
11、算术表达式三种表示: (1)中缀(infix)表示: 例如A+B (2)前缀(prefix)表示: 例如+ AB (3)后缀(postfix)表示: 例如AB +,栈与队列,36/132,中缀表示,平时所用的表达式都是中缀表示。如A+B*(C-D)-E/F中缀表示中有操作符的优先级问题,并可以加括号改变运算顺序。用中缀表示求解表达式的值需要利用两个栈来实现,一个暂存操作数,另一个暂存操作符。,栈与队列,37/132,计算中缀表达式算法思想,设置两个工作栈 运算符栈OPTR,运算符栈的栈底为表达式的起始符#。 操作数栈OPND,操作数栈为空栈。,栈与队列,38/132,计算中缀表达式算法思想,设
12、isp(.)代表栈内优先级,icp(.)代表栈外优先级 依次读入表达式中的每个字符,若是 操作数,则进OPND栈; 运算符s1,则和OPTR中的栈顶元素s2做比较再操作。 若icp(s1)isp(s2),则运算符入栈; 若icp(s1)isp(s2),则从OPND栈顶弹出两个操作数,与OPTR中的栈顶元素做运算,并将运算结果入OPND; 若icp(s1)=isp(s2),则OPTR中的栈顶元素出栈。 直至表达式扫描完毕。,栈与队列,39/132,各个算术操作符的优先级,操作符 # ( *,/,% +,- ),isp 0 1 7 5 3 8,icp 0 8 6 4 2 1,栈与队列,40/132
13、,如: x = 3 (7-2);,OPND栈,OPTR栈,执行过程:# 3 (7-2) #,#,计算中缀表达式的值,栈与队列,41/132,如: x = 3 (7-2),OPND栈,OPTR栈,执行过程:# 3 (7-2) #,#,3,计算中缀表达式的值,栈与队列,42/132,如: x = 3 (7-2),OPND栈,OPTR栈,执行过程:# 3 (7-2) #,#,3,计算中缀表达式的值,栈与队列,43/132,如: x = 3 (7-2),OPND栈,OPTR栈,执行过程:# 3 (7-2) #,#,3,计算中缀表达式的值,栈与队列,44/132,如: x = 3 (7-2),OPND栈
14、,OPTR栈,执行过程:# 3 (7-2) #,#,3,计算中缀表达式的值,栈与队列,45/132,如: x = 3 (7-2),OPND栈,OPTR栈,执行过程:# 3 (7-2) #,#,3,(,计算中缀表达式的值,栈与队列,46/132,如: x = 3 (7-2),OPND栈,OPTR栈,执行过程:# 3 (7-2) #,#,3,(,计算中缀表达式的值,栈与队列,47/132,如: x = 3 (7-2),OPND栈,OPTR栈,执行过程:# 3 (7-2) #,#,3,(,7,计算中缀表达式的值,栈与队列,48/132,如: x = 3 (7-2),OPND栈,OPTR栈,执行过程:
15、# 3 (7-2) #,#,3,(,7,计算中缀表达式的值,栈与队列,49/132,如: x = 3 (7-2),OPND栈,OPTR栈,执行过程:# 3 (7-2) #,#,3,(,7,-,计算中缀表达式的值,栈与队列,50/132,如: x = 3 (7-2),OPND栈,OPTR栈,执行过程:# 3 (7-2) #,#,3,(,7,-,2,计算中缀表达式的值,栈与队列,51/132,如: x = 3 (7-2),OPND栈,OPTR栈,执行过程:# 3 (7-2) #,#,3,(,7,-,2,计算中缀表达式的值,栈与队列,52/132,如: x = 3 (7-2),OPND栈,OPTR栈
16、,执行过程:# 3 (7-2) #,#,3,(,7,-,2,),计算中缀表达式的值,栈与队列,53/132,如: x = 3 (7-2),OPND栈,OPTR栈,执行过程:# 3 (7-2) #,#,3,(,5,-,),计算中缀表达式的值,栈与队列,54/132,如: x = 3 (7-2),OPND栈,OPTR栈,执行过程:# 3 (7-2) #,#,3,(,5,),计算中缀表达式的值,栈与队列,55/132,如: x = 3 (7-2),OPND栈,OPTR栈,执行过程:# 3 (7-2) #,#,3,(,5,),计算中缀表达式的值,栈与队列,56/132,如: x = 3 (7-2),O
17、PND栈,OPTR栈,执行过程:# 3 (7-2) #,#,3,(,5,),计算中缀表达式的值,栈与队列,57/132,如: x = 3 (7-2),OPND栈,OPTR栈,执行过程:# 3 (7-2) #,#,3,5,计算中缀表达式的值,栈与队列,58/132,如: x = 3 (7-2),OPND栈,OPTR栈,执行过程:# 3 (7-2) #,#,3,5,计算中缀表达式的值,栈与队列,59/132,如: x = 3 (7-2),OPND栈,OPTR栈,执行过程:# 3 (7-2) #,#,3,5,#,计算中缀表达式的值,栈与队列,60/132,如: x = 3 (7-2),OPND栈,O
18、PTR栈,执行过程:# 3 (7-2) #,#,15,#,计算中缀表达式的值,栈与队列,61/132,如: x = 3 (7-2),OPND栈,OPTR栈,执行过程:# 3 (7-2) #,#,15,#,计算中缀表达式的值,栈与队列,62/132,如: x = 3 (7-2),OPND栈,OPTR栈,执行过程:# 3 (7-2) #,#,15,#,计算中缀表达式的值,栈与队列,63/132,后缀表达式,后缀表示也叫做 RPN或逆波兰记号,参加运算的操作数总在操作符前面。利用后缀表示求解表达式的值时,从左向右顺序地扫描表达式。后缀表达式中不出现括号。后缀表达式例子: 3 7 2 - * 10 +
19、,栈与队列,64/132,后缀表达式计算,顺序扫描表达式的每一项, 操作数,压入栈中;操作符,连续从栈中退出两个操作数Y 和X,形成运算指令 XY,并将结果重新压入栈中。当扫描完表达式后,栈顶存放的就是计算结果。,栈与队列,65/132,计算后缀表达式的值,A B C D - * + E F / -R1 R4R2R3R5等价于:A+B*(C-D)-E/F,用后缀表达式计算的步骤,步 扫描项 项类型 动作 栈中内容,1 置空栈 空,2 A operand 进栈 A,3 B operand 进栈 A B,4 C operand 进栈 A B C,5 D operand 进栈 A B C D,6 -
20、 operator D和C退栈,计算C-D,结果R1进栈 A B R1,7 * operand R1和B退栈,计算B*R1,结果R2进栈 A R2,8 + operand R2和A退栈,计算A+R2,结果R3进栈 R3,9 E operand 进栈 R3 E,10 F operand 进栈 R3 E F,11 / operand F和E退栈,计算E/F,结果R4进栈 R3 R4,12 - operand R4和R3退栈,计算R3-R4,结果R5进栈 R5,A B C D - * + E F / -,栈与队列,67/132,中缀向后缀转换,扫描中缀表达式, 操作数直接输出; 操作符放到(操作符)
21、栈中。如:A+B*(C-D)-E/F 应转换为ABCD-*+EF/-,算法过程?,栈与队列,68/132,a=b+(c-d)*(e-f); g10=h i9 +(j+k)*l; while( m(n8+o) ) p=7; /* initialize p */ r=6; ,更多:匹配程序中的分隔符, 在程序中匹配分隔符 分隔符:圆括号( ); 方括号 ; 花括号 ; 注释分隔符/* */.,特大数加法(乘法),4376,592 + 3784,Operand- Stack1,Operand- Stack2,result- Stack,5,9,2,3,7,8,4,栈与队列,70/132,栈与递归,概
22、念 例子 演示 递归与循环,栈与队列,71/132,栈与递归,函数调用与递归实现每个函数(包括主函数main())的状态的决定因素: 函数中所有自动变量的内容 函数参数的值 返回地址包含这些信息的数据区称为活动记录或者栈结构,它是在运行栈上分配空间的。,栈与队列,72/132,栈与递归,活动记录通常包含以下信息: 函数所有参数的值;可以存储在其他地方的局部变量(自动变量),活动记录只包含它们的描述符和指向存放它们的位置的指针。回到调用程序的返回地址,即在调用完成之后紧接着此函数调用指令之后的指令的地址。一个指向调用程序的活动记录的动态链接指针。非void类型的函数的返回值。,栈与队列,73/1
23、32,main(),f1(),f2(),f3(),栈与队列,74/132,栈与递归实例,递归调用的剖析 定义一个数x的非负整数n次幂的函数是递归函数的一个很好的例子。这个函数最直接的定义是:可以直接根据幂的定义写出计算xn的C+函数: /*102*/ double power (double x, unsigned int n) /*103*/ if (n=0) /*104*/ return 1.0;/else /*105*/ return x*power (x,n-1);,栈与队列,75/132,栈与递归,使用这个定义,x4的值可以按照下面的方式计算:,栈与队列,76/132,栈与递归,或者
24、表示为下面的形式:,栈与队列,77/132,栈与递归实例2,函数power()通过以下语句在主函数main()中调用:int main() /*136*/ y=power(5.6, 2) ,栈与队列,78/132,栈与递归实例2,递归调用的追踪:第1次调用 power(5.6, 2) 第2次调用 power(5.6, 1) 第3次调用 power(5.6, 0) 第3次调用 1 第2次调用 5.6 第1次调用 31.36,栈与队列,79/132,对power()的 第三次调用,对power()的 第二次调用,对power()的 第一次调用,Main()的 活动记录,注:SP 栈指针AR 活动记
25、录? 为返回值保留的位置,/*102*/ double power (double x, unsigned int n) /*103*/ if (n=0) /*104*/ return 1.0;/else /*105*/ return x*power (x,n-1);,int main() /*136*/ y=power(5.6, 2) ,栈与递归演示,栈与队列,80/132,对power()的 第三次调用,对power()的 第二次调用,对power()的 第一次调用,Main()的 活动记录,注:SP 栈指针AR 活动记录? 为返回值保留的位置,/*102*/ double power (
26、double x, unsigned int n) /*103*/ if (n=0) /*104*/ return 1.0;/else /*105*/ return x*power (x,n-1);,int main() /*136*/ y=power(5.6, 2) ,栈与递归演示,栈与队列,81/132,对power()的 第三次调用,对power()的 第二次调用,对power()的 第一次调用,Main()的 活动记录,注:SP 栈指针AR 活动记录? 为返回值保留的位置,/*102*/ double power (double x, unsigned int n) /*103*/ i
27、f (n=0) /*104*/ return 1.0;/else /*105*/ return x*power (x,n-1);,int main() /*136*/ y=power(5.6, 2) ,栈与递归演示,栈与队列,82/132,对power()的 第三次调用,对power()的 第二次调用,对power()的 第一次调用,Main()的 活动记录,注:SP 栈指针AR 活动记录? 为返回值保留的位置,/*102*/ double power (double x, unsigned int n) /*103*/ if (n=0) /*104*/ return 1.0;/else /*
28、105*/ return x*power (x,n-1);,int main() /*136*/ y=power(5.6, 2) ,栈与递归演示,栈与队列,83/132,对power()的 第三次调用,对power()的 第二次调用,对power()的 第一次调用,Main()的 活动记录,注:SP 栈指针AR 活动记录? 为返回值保留的位置,/*102*/ double power (double x, unsigned int n) /*103*/ if (n=0) /*104*/ return 1.0;/else /*105*/ return x*power (x,n-1);,int m
29、ain() /*136*/ y=power(5.6, 2) ,栈与递归演示,栈与队列,84/132,对power()的 第三次调用,对power()的 第二次调用,对power()的 第一次调用,Main()的 活动记录,注:SP 栈指针AR 活动记录? 为返回值保留的位置,/*102*/ double power (double x, unsigned int n) /*103*/ if (n=0) /*104*/ return 1.0;/else /*105*/ return x*power (x,n-1);,int main() /*136*/ y=power(5.6, 2) ,栈与递归
30、演示,栈与队列,85/132,对power()的 第三次调用,对power()的 第二次调用,对power()的 第一次调用,Main()的 活动记录,注:SP 栈指针AR 活动记录? 为返回值保留的位置,/*102*/ double power (double x, unsigned int n) /*103*/ if (n=0) /*104*/ return 1.0;/else /*105*/ return x*power (x,n-1);,int main() /*136*/ y=power(5.6, 2) ,栈与递归演示,栈与队列,86/132,有A,B,C三个塔座,A上套有n个直径不
31、同的圆盘,按直径从小到大叠放,形如宝塔,编号1,2,3n。要求将n个圆盘从A移到C,叠放顺序不变,移动过程中遵循下列原则: 每次只能移一个圆盘; 圆盘可在三个塔座上任意移动; 任何时刻,每个塔座上不能将大盘压到小盘上。,Tower of Hanoi问题,解决方法: n=1时,直接把圆盘从A移到C。 n1时 把上面n-1个圆盘从A移到B 然后将n号盘从A移到C 将n-1个盘从B移到C。 即把解n个圆盘的Hanoi问题转化为解n-1个圆盘的Hanoi问题,依次,直至转化成只有一个圆盘的Hanoi问题。,栈与队列,88/132,递归与循环,递归函数可以使用循环来实现。 函数Power()可以用另一种
32、方法来实现:double nonRecPower(double x, unsigned int n)double result=1;for(result=x;n1;-n)result*=x;return result;,栈与队列,89/132,尾部递归,尾部递归的特点 在每个函数的实现的末尾只使用一个递归调用 例如:void tail(int i) if(i0) couti ;tail(i-1); ,栈与队列,90/132,尾部递归,尾部递归只是一个变形的循环 void iterativeTail(int i) for(; i0;i-)couti“ ”; ,栈与队列,91/132,非尾部递归,
33、例如:将输入行以相反的顺序打印出来。void reverse()char ch;cin.get(ch);if(ch!=n)reverse();cout.put(ch);,栈与队列,92/132,非尾部递归,非尾部递归转换成非递归: 非尾部递归转化为迭代形式需要对栈进行显示处理。 void nonRecursiveReverse() int ch;cin.get(ch);while(ch!=n)st.push(ch); /st是一个全局栈对象cin.get(ch); while(!st.empty()cout.put(st.pop(); ,栈与队列,93/132,递归过程改为非递归过程,递归过程
34、简洁、易编、易懂; 递归过程效率低,重复计算多; 改为非递归过程的目的是提高效率; 单向递归和尾递归可直接用迭代实现其非递归过程; 其他情形必须借助栈实现非递归过程。,栈与队列,94/132,队列,先进先出(first in/first out, FIFO)线性结构。 队首:删除端 队尾:插入端,栈与队列,95/132,队列操作,clear() 清空队列。 isEmpty() 判断队列是否为空。 enqueue(el) 队列的尾部加入元素el。 dequeue() 取出队列的第一个元素。 firstEl() 返回队列的第一个元素,但不移去它。,栈与队列,96/132,队列,顺序方式表示和实现链
35、式方式表示和实现循环队列队列的应用,栈与队列,97/132,队列的顺序表示1,用数组queueMaxSize描述一个队列, location(i)=i-1;,队空:front=0; rear=-1;,插入A元素: front=0; rear=0;,插入B元素: front=0; rear=1;,栈与队列,98/132,队列的顺序表示1,A,B,C,D,队列满: rear=MaxSize-1,出队, front=1;,插入E: rear=MaxSize-1; 队列满,不能插入,假溢出!空间浪费!,栈与队列,99/132,队列的顺序表示1,A,B,C,D,队列满: rear=MaxSize-1,B
36、,C,D,出队, front=1;,B,C,D,出队后调整: front=0; rear=rear-1;,浪费 时间!,栈与队列,100/132,队列假溢出解决方案,循环队列 基本思想:把队列设想成环形,让sq0接在sqM-1之后,若rear+1=M,则令rear=0; 入队: rear=(rear+1)%M; sqrear=x; 出队: front=(front+1)%M; x=sqfront;,栈与队列,101/132,解决方案: 1.另外设一个标志以区别队空、队满 2.少用一个元素空间:队空:front=rear队满:(rear+1)%M=front,队空:front=rear,队满:f
37、ront=rear,栈与队列,102/132,循环队列,队列空: front=rear;,B,C,D,队列满: (rear+1)%MaxSize=front,栈与队列,103/132,循环队列,B,C,D,出队: front= (front+1)%MaxSize,进队: rear= (rear+1)%MaxSize,栈与队列,104/132,队列中结点的个数,队中结点的个数:( rear - front + maxSize ) % maxSize,栈与队列,105/132,链式队列, 链式队列:front 和 rear 分别是队首和队尾指针。,data link,队首结点,front,队尾结点
38、,rear,栈与队列,106/132,链式队列的操作,空队列:front = rear = NULL,栈与队列,107/132,打印多项式(a+b)i的系数:,1 1,1 2 1,1 3 3 1,1 4 6 4 1,1 5 10 10 5 1,1 6 15 20 15 6 1,i=1,2,3,4,5,6,队列的应用,栈与队列,108/132,队列的应用,0 1 2 1 0,0 1 3 3 1 0,0 1 4 6 4 1 0,栈与队列,109/132,队列的应用,1 2 1 0 1 3 3 1 0 1,0,栈与队列,110/132,小结,栈的特点、定义、操作、应用栈和递归、递归和循环队列的特点、
39、定义、操作、应用,栈与队列,111/132,上机作业,给出顺序栈、队列定义 后缀表达式计算 匹配程序中分隔符 特大数加法 打印多项式(a+b)i的系数 上机题 5 选作 中缀表达式转换成后缀表达式,栈与队列,112/132,文档作业,练习题 6,7,9,10,栈与队列,113/132,后续内容,第4章 字符串,栈与队列,114/132,迷宫求解,思想:,使用栈记录走过的路径,若当前位置不通,标记该位置,退栈,寻找新路经,若当前位置通,进栈,寻找新路经,栈与队列,115/132,迷宫算法描述,设入口位置为当前位置;,若当前位置可通,则将当前位置进栈;,若该位置是出口位置,则成功;,否则置当前位置的东邻方块为新的当前位置;,否则,,若栈不空且栈顶位置上有其他方向未经探索,则置当前位置的下一相邻方块为新的当前位置。,若栈不空且栈顶位置的四周都不通,则栈顶出栈,标记不通;重新测试新的栈顶位置,直至找到一个可通的相邻块作为新的当前位置。,栈与队列,116/132,初始,1.1当前位置,可通,进栈,1.2当前位置,可通,进栈,1.3不通,2.2为当前位置,进栈,北,南,东,西,2.3不通,3.2为当前位置,进栈,3.3可通,进栈,3.4、4.3、3.2、2.3均不通,3.3出栈,3.2为新栈顶元素,3.3、4.2不通,3.1为当前位置,进栈,