栈和队列这一章讲的也是两种非常重要的线性结构,他们也属于线性表,只是在理解线性表后理解站和队列会比较得心应手
先来看下栈吧,就像手 枪装子弹一样,先放进去的子弹在最下面,最后放进去的子弹为第一枪打出来,栈就是弹夹这个结构,经典说法就是后进先出(last in first out),简称LIFO,和线性表一样,栈也有ADT即抽象数据类型的定义,也有两种表示方法,顺序栈和链式栈,一般顺序栈用的较多
Typedef struct{
SElemType *base;
SElemTypr *top;
Int stacksize;
}SqStack;
这里用了两个指针top表示栈顶指针,base表示栈底指针,当top=base时表示空栈。
函数我就不整理了,在具体例题里面体现吧,因为栈的例题还有一部分是很经典的,比如算法3-1数制转换问题,例如将十进制数转化为八进制,1348=2504。我们知道用除余法可以解决这个问题,但是先出来的余数要放在最下面,即如图
具体实现代码我用了函数封装来实现:如
图示为头文件c3-1.h和算法3.1的程序,在头文件中,定义了一个顺序栈,在算法中调用出来
很简单就是一个先初始化栈,然后用push函数入栈n%8求余,然后n保存除以8之后的值,出栈的时候用pop函数弹出栈顶元素,然后输出。
在代码中用了很多栈的函数如pop,push等,其实可以#include <stack>里面都包含了,但是还是自己实现下会更深刻,我整理如下:
这个bo3-1.cpp也是在算法函数中要调用的,工具函数要实现还是要在头文件中申明函数的,在栈函数中,其中的追加空间也是很重要的,即判断栈满了是否要追加空间,否则程序不能顺利进行。
3.2.2是一个括号匹配的检验问题,也是一个进栈出栈的问题,如果进栈的括号和前一括号匹配则前一括号出栈如果不匹配则进栈,这样就实现了较外层的括号永远在下面优先级就低于内层括号。输入括号可以用gets(ch),然后设置一个指针*p指向ch,循环p发现左括号就push,发现右括号就pop完整代码如下:
Case:后面没有东西说明多个case可以共用一组执行语句,这是多分支选择结构switch的一个优点,default表示其他,后面可以不用接break,check()函数也不难,一个while和一个switch循环选择,最后判断的时候如果栈是空的,即刚好全部匹配完pop后,用if(stackempty(s))来判断,这道题也不难。
行编辑程序也是一个简单的栈应用题目,接下来看下最著名的迷宫求解题目
先分析题目,这个是我做的一个迷宫(有点恐怖,红色的代表通路,黑色的代表墙白色的代表出入口)
用函数定义结构体函数postype(行x列y)来表示每个格子,如上图的是10行10列
用MazeType[][]数组来存放数据,begin,end 表示入口出口。在初始化迷宫之前约定:
0代表黑色的墙 1代表可以通行 -1代表不可以通行
东南西北一次为 0 1 2 3
初始化迷宫,然后全部赋值为k(通道),除了外墙赋值为0。然后输入墙的值一个迷宫就建好了。
前期准备工作代码如下:
接下来实现栈的操作,一样的定义结构体栈Sqstack,栈的元素类型,struct SElemType 里面包括通道块在路径上的“序号”,通道快在迷宫中的“坐标位置”,下一个位置的方向。
定义pass函数,判断是否可以通过,定义footprint函数,追踪现在的位置,Markprint函数来标记此处不能通过,Nextpos函数实现方向的选择,基本函数就这些了,主代码主要还是运用了栈来求解,从开始位置顺时针选择方向走下去,如果可以通过则入栈,碰到墙就pop然后顺时针换方向,一直到end为止,代码如下:
接下来就是实现主函数了,这个函数才真正体现栈的强大,Mazepath函数,首先把start放在当前位置curpos上,然后开始循环,用pass函数判断是否通过,通过的话就Footprint留下足迹,记下坐标
然后设置为1即可以通过,然后方向置为0开始循环,将这些信息封装好入栈,然后足迹cursetp加1,如果到达终点的话,就返回true否则就用nextpos来确定下一个方向,这事pass的函数,如果不pass即当前位置不通过,就要之行pop操作了,同样上面的操作不过就是足迹减一,然后pop弹出,然后标记-1,如果还有方向可以换,就入栈下一个方向,同样记录有关数据,用nextpos来记录下一个位置的坐标。主函数mazeopath的代码如下:
这就是完整的迷宫求解问题了。(程序员是命苦= =)