1. 引言
本文主要讲解栈的一些应用:(1)迷宫求解 (2)表达式求值 (3)栈与递归 ——hanoi塔(4)括号匹配
2. 迷宫求解
2.1 解法1
#include <stdio.h>
// test
#if 0
#define MAZE_COLUMN 7 // 迷宫的列数
#define MAZE_ROW 7 // 迷宫的行数
//在二维阵列中使用2表示迷宫墙壁,使用0来表示老鼠的行走路径
int m[MAZE_ROW][MAZE_COLUMN] = {
{2, 2, 2, 2, 2, 2, 2},
{2, 0, 0, 0, 0, 0, 2},
{2, 0, 2, 0, 2, 0, 2},
{2, 0, 0, 2, 0, 2, 2},
{2, 2, 0, 2, 0, 2, 2},
{2, 0, 0, 0, 0, 0, 2},
{2, 2, 2, 2, 2, 2, 2}
};
#else
#define MAZE_COLUMN 5 // 迷宫的列数
#define MAZE_ROW 5 // 迷宫的行数
//在二维阵列中使用2表示迷宫墙壁,使用0来表示老鼠的行走路径
int m[MAZE_ROW][MAZE_COLUMN] = {
{2, 2, 2, 2, 2},
{2, 0, 0, 0, 2},
{2, 0, 2, 0, 2},
{2, 0, 0, 0, 2},
{2, 2, 2, 2, 2}
};
#endif
struct PosType // 迷宫坐标位置类型
{
int x; // 行值
int y; // 列值
};
PosType begin,end; // 迷宫的入口坐标,出口坐标
int curstep=1; // 当前足迹,初值(在入口处)为1
// 输出迷宫的解(m数组)
void Print()
{
int i, j;
for(i=0; i<MAZE_ROW; i++)
{
for(j=0; j<MAZE_COLUMN; j++)
printf("%3d", m[i][j]);
printf("\n");
}
}
// 由当前位置cur,当前步骤curstep试探下一点
void try_path(PosType cur, int curstep)
{
int i;
PosType next; // 下一个位置
PosType direc[4] = {{0,1}, {1,0}, {0,-1}, {-1,0}}; // {行增量,列增量},移动方向依次无为东南西北
// 依次试探东南西北四个方向
for (i = 0; i <= 3; i++)
{
// 根据移动方向,给下一位置赋值
next.x = cur.x + direc[i].x;
next.y = cur.y + direc[i].y;
if (m[next.x][next.y] == 0) // 下一个位置是通道
{
m[next.x][next.y] = ++curstep;
if (next.x != end.x || next.y != end.y) // 没有到达终点
try_path(next, curstep);
else // 到达终点
{
Print(); // 输出结果(出口,不再递归调用)
printf("\n");
}
m[next.x][next.y] = 0; // 恢复为通路,以便在另一个方向试探另一条路
curstep--;
}
}
}
int main()
{
printf("请输入入口的行数,列数:");
scanf("%d,%d",&begin.x,&begin.y);
printf("请输入出口的行数,列数:");
scanf("%d,%d",&end.x,&end.y);
printf("此迷宫从入口到出口的路径如下:\n");
m[begin.x][begin.y] = 1; // 入口的足迹为1
try_path(begin, 1);
return 0;
}
2.2 解法2
#include "ds.h"
#define STACK_INIT_SIZE 10 // 栈存储空间初始分配量
#define STACK_INCREMENT 2 // 栈存储空间分配增量
#define MAZE_COLUMN 7 // 迷宫的列数
#define MAZE_ROW 7 // 迷宫的行数
struct PosType // 迷宫坐标位置类型
{
int x; // 行值
int y; // 列值
};
struct SElemType // 栈的元素类型
{
int ord; // 通道块在路径上的"序号"
PosType seat; // 通道块在迷宫中的"坐标位置"
int di; // 从此通道块走向下一通道块的"方向"(0~3表示东~北)
};
typedef struct SqStack
{
SElemType *base; // 在栈构造之前和销毁之后,base的值为NULL
SElemType *top; // 栈顶指针
int stacksize; // 当前已分配的存储空间,以元素为单位
}SqStack;
//在二维阵列中使用2表示迷宫墙壁,使用0来表示老鼠的行走路径
int m[MAZE_ROW][MAZE_COLUMN] = {
{2, 2, 2, 2, 2, 2, 2},
{2, 0, 0, 0, 0, 0, 2},
{2, 0, 2, 0, 2, 0, 2},
{2, 0, 0, 2, 0, 2, 2},
{2, 2, 0, 2, 0, 2, 2},
{2, 0, 0, 0, 0, 0, 2},
{2, 2, 2, 2, 2, 2, 2}
};
PosType begin,end; // 迷宫的入口坐标,出口坐标
SqStack S; // 顺序栈
int curstep=1; // 当前足迹,初值(在入口处)为1
// 构造一个空栈S
void InitStack(SqStack &S)
{
S.base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType));
if (!S.base) exit(OVERFLOW);
S.top = S.base;
S.stacksize = STACK_INIT_SIZE;
}
// 若栈S为空栈,则返回TRUE,否则返回FALSE
Status StackEmpty(SqStack S)
{
if (S.top == S.base)
return TRUE;
else
return FALSE;
}
// 插入元素e为新的栈顶元素
void Push(SqStack &S, SElemType e)
{
if (S.top - S.base >= S.stacksize)
{
S.base = (SElemType*)realloc(S.base, (S.stacksize + STACK_INCREMENT) * sizeof(SElemType));
if (!S.base) exit(OVERFLOW);
S.top = S.base + S.stacksize;
S.stacksize += STACK_INCREMENT;
}
memcpy(S.top, &e, sizeof(SElemType));
S.top++;
}
// 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
Status Pop(SqStack &S, SElemType &e)
{
if (S.top == S.base)
return ERROR;
memcpy(&e, --S.top, sizeof(SElemType));
return OK;
}
// 从栈底到栈顶依次对栈中每个元素调用函数visit()
void StackTraverse(SqStack S, void(* visit)(SElemType))
{
SElemType *p = S.base;
while(p < S.top)
{
visit(*p++);
printf("\n");
}
printf("\n");
}
void print(SElemType e)
{
printf("序号:%d \t",e.ord);
printf("坐标位置:(%d, %d) \t",e.seat.x, e.seat.y);
printf("方向(0~3表示东~北): %d\n", e.di);
}
void Print()
{ // 输出迷宫的解(m数组)
int i, j;
for(i=0; i<MAZE_ROW; i++)
{
for(j=0;j<MAZE_COLUMN;j++)
printf("%3d",m[i][j]);
printf("\n");
}
}
int Pass(PosType b)
{
// 当迷宫m的b点的序号为0(可通过路径),返回1;否则,返回0
if(m[b.x][b.y] == 0)
return 1;
else
return 0;
}
void FootPrint(PosType a)
{
// 使迷宫m的a点的值变为足迹(curstep)
m[a.x][a.y] = curstep;
}
void NextPos(PosType &c, int di)
{
// 根据当前位置及移动方向,求得下一位置
PosType direc[4] = {{0,1}, {1,0}, {0,-1}, {-1,0}}; // {行增量,列增量},移动方向,依次为东南西北
c.x += direc[di].x;
c.y += direc[di].y;
}
void MarkPrint(PosType b)
{
// 使迷宫m的b点的序号变为-1(不能通过的路径)
m[b.x][b.y] = -1;
}
Status MazePath(PosType start,PosType end) // 算法3.3
{ // 若迷宫m中存在从入口start到出口end的通道,则求得一条
// 存放在栈中(从栈底到栈顶),并返回TRUE;否则返回FALSE
PosType curpos; // 当前位置
SElemType e; // 栈元素
InitStack(S); // 初始化栈
curpos = start; // 当前位置在入口
do
{
// 当前位置可以通过,即是未曾走到过的通道块
if(Pass(curpos))
{
FootPrint(curpos); // 留下足迹
e.ord = curstep;
e.seat = curpos;
e.di = 0;
Push(S,e); // 入栈当前位置及状态
curstep++; // 足迹加1
if(curpos.x == end.x && curpos.y == end.y) // 到达终点(出口)
return TRUE;
NextPos(curpos,e.di); // 由当前位置及移动方向,确定下一个当前位置
}
else
{
// 当前位置不能通过
if(!StackEmpty(S)) // 栈不空
{
Pop(S,e); // 退栈到前一位置
curstep--; // 足迹减1
while(e.di==3&&!StackEmpty(S)) // 前一位置处于最后一个方向(北)
{
MarkPrint(e.seat); // 在前一位置留下不能通过的标记(-1)
Pop(S,e); // 再退回一步
curstep--; // 足迹再减1
}
if(e.di<3) // 没到最后一个方向(北)
{
e.di++; // 换下一个方向探索
Push(S,e); // 入栈该位置的下一个方向
curstep++; // 足迹加1
curpos=e.seat; // 确定当前位置
NextPos(curpos,e.di); // 确定下一个当前位置是该新方向上的相邻块
}
}
}
}while(!StackEmpty(S));
return FALSE;
}
int main()
{
printf("请输入入口的行数,列数:");
scanf("%d,%d",&begin.x,&begin.y);
printf("请输入出口的行数,列数:");
scanf("%d,%d",&end.x,&end.y);
if (MazePath(begin, end))
{
printf("此迷宫从入口到出口的一条路径如下 \n");
Print(); // 输出此通路
}
else
{
printf("此迷宫没有从入口到出口的路径 \n");
}
printf("到达终点时栈的内容: \n");
StackTraverse(S, print);
}
3. 表达式求值
#include "ds.h"
#define STACK_INIT_SIZE 10 // 存储空间初始分配量
#define STACK_INCREMENT 2 // 存储空间分配增量
typedef char SElemType;
typedef struct SqStack
{
SElemType *base; // 在栈构造之前和销毁之后,base的值为NULL
SElemType *top; // 栈顶指针
int stacksize; // 当前已分配的存储空间,以元素为单位
}SqStack;
void InitStack(SqStack &S);
void DestroyStack(SqStack &S);
void ClearStack(SqStack &S);
Status StackEmpty(SqStack S);
int StackLength(SqStack S);
Status GetTop(SqStack S, SElemType &e);
void Push(SqStack &S, SElemType e);
Status Pop(SqStack &S, SElemType &e);
void StackTraverse(SqStack S, void(* visit)(SElemType));
// 构造一个空栈S
void InitStack(SqStack &S)
{
S.base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType));
if (!S.base) exit(OVERFLOW);
S.top = S.base;
S.stacksize = STACK_INIT_SIZE;
}
// 销毁栈S,S不再存在
void DestroyStack(SqStack &S)
{
free(S.base);
S.base = NULL;
S.top = NULL;
S.stacksize = 0;
}
// 把S置为空栈
void ClearStack(SqStack &S)
{
S.top = S.base;
}
// 若栈S为空栈,则返回TRUE,否则返回FALSE
Status StackEmpty(SqStack S)
{
if (S.top == S.base)
return TRUE;
else
return FALSE;
}
// 返回S的元素个数,即栈的长度
int StackLength(SqStack S)
{
return S.top - S.base; // not return S.stacksize;
}
// 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR
Status GetTop(SqStack S, SElemType &e)
{
if (S.top > S.base)
{
memcpy(&e, S.top - 1, sizeof(SElemType));
return OK;
}
else
{
return ERROR;
}
}
// 插入元素e为新的栈顶元素
void Push(SqStack &S, SElemType e)
{
if (S.top - S.base >= S.stacksize)
{
S.base = (SElemType*)realloc(S.base, (S.stacksize + STACK_INCREMENT) * sizeof(SElemType));
if (!S.base) exit(OVERFLOW);
S.top = S.base + S.stacksize;
S.stacksize += STACK_INCREMENT;
}
memcpy(S.top, &e, sizeof(SElemType));
S.top++;
}
// 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
Status Pop(SqStack &S, SElemType &e)
{
if (S.top == S.base)
return ERROR;
memcpy(&e, --S.top, sizeof(SElemType));
return OK;
}
// 从栈底到栈顶依次对栈中每个元素调用函数visit()
void StackTraverse(SqStack S, void(* visit)(SElemType))
{
SElemType *p = S.base;
while(p < S.top)
{
visit(*p++);
}
printf("\n");
}
void print(SElemType c)
{
printf("%c ",c);
}
char Precede(SElemType t1, SElemType t2)
{
char ret;
switch (t2)
{
case '+':
case '-':
if (t1 == '(' || t1 == '\n')
ret = '<'; // t1 < t2
else
ret = '>'; // t1 > t2
break;
case '*':
case '/':
if (t1 == '*' || t1 == '/' || t1 == ')')
ret = '>'; // t1 > t2
else
ret = '<'; // t1 < t2
break;
case '(':
if (t1 == ')')
{
printf("括号不匹配 \n");
exit(ERROR);
}
else
ret = '<'; // t1 < t2
break;
case ')':
switch (t1)
{
case '(':
ret = '='; // t1 = t2
break;
case '\n':
printf("缺乏左括号 \n");
exit(ERROR);
default:
ret = '>'; // t1 > t2
}
break;
case '\n':
switch (t1)
{
case '\n':
ret = '='; // t1 = t2
break;
case '(':
printf("缺乏右括号\n");
exit(ERROR);
default:
ret = '>'; // t1 > t2
}
}
return ret;
}
Status In(SElemType c)
{
switch (c)
{
case '+':
case '-':
case '*':
case '/':
case '(':
case ')':
case '\n':
return TRUE;
default:
return FALSE;
}
}
SElemType Operate(SElemType a, SElemType theta, SElemType b)
{
switch (theta)
{
case '+': return a+b;
case '-': return a-b;
case '*': return a*b;
}
return a/b;
}
// 算术表达式求值的算符优先算法。设OPTR和OPND分别为运算符栈和运算数栈
SElemType EvaluateExpression()
{
SqStack OPTR,OPND;
SElemType a,b,d,x;
char c; // 存放由键盘接收的字符,
char z[11]; // 存放整数字符串,
int i;
InitStack(OPTR); // 初始化运算符栈OPTR和运算数栈OPND
InitStack(OPND);
Push(OPTR,'\n'); // 将换行符压入运算符栈OPTR的栈底(改)
c=getchar(); // 由键盘读入1个字符到c
GetTop(OPTR,x); // 将运算符栈OPTR的栈顶元素赋给x
while(c!='\n'||x!='\n') // c和x不都是换行符
{
if(In(c)) // c是7种运算符之一
switch(Precede(x,c)) // 判断x和c的优先权
{
case'<' :Push(OPTR,c); // 栈顶元素x的优先权低,入栈c
c=getchar(); // 由键盘读入下一个字符到c
break;
case'=' :Pop(OPTR,x); // x='('且c=')'情况,弹出'('给x(后又扔掉)
c=getchar(); // 由键盘读入下一个字符到c(扔掉')')
break;
case'>' :Pop(OPTR,x); // 栈顶元素x的优先权高,弹出运算符栈OPTR的栈顶元素给x(改)
Pop(OPND,b); // 依次弹出运算数栈OPND的栈顶元素给b,a
Pop(OPND,a);
Push(OPND,Operate(a,x,b)); // 做运算a x b,并将运算结果入运算数栈
}
else if(c>='0'&&c<='9') // c是操作数,此语句改
{
i=0;
while(c>='0'&&c<='9') // 是连续数字
{
z[i++]=c;
c=getchar();
}
z[i]=0; // 字符串结束符
d=atoi(z); // 将z中保存的数值型字符串转为整型存于d
Push(OPND,d); // 将d压入运算数栈OPND
}
else // c是非法字符,以下同
{
printf("出现非法字符\n");
exit(ERROR);
}
GetTop(OPTR,x); // 将运算符栈OPTR的栈顶元素赋给x
}
Pop(OPND,x); // 弹出运算数栈OPND的栈顶元素(运算结果)给x(改此处)
if(!StackEmpty(OPND)) // 运算数栈OPND不空(运算符栈OPTR仅剩'\n')
{
printf("表达式不正确\n");
exit(ERROR);
}
return x;
}
int main()
{
printf("请输入算术表达式,负数要用(0-正数)表示\n");
printf("%d\n",EvaluateExpression());
}
4. 栈与递归 ——hanoi塔
#include <stdio.h>
int move_times = 0; // 全局变量, 搬动次数
// 第n块圆盘从塔座x 搬到塔座z
void move(char x, int n, char z)
{
printf("第%i步: 将%i号圆盘从%c移到%c\n", ++move_times, n, x, z);
}
// 将塔座x上按直径由小到大且自上而下编号为1至n的n个圆盘
// 按规则搬到塔座z上,y可以做辅助塔座
void hanoi(int n, char x, char y, char z)
{
// 出口
if (1 == n)
{
move(x, 1, z);
}
else
{
hanoi(n-1, x, z, y); // 将x上编号为1至n-1的圆盘移动y上,z做辅助塔座(降阶递归调用)
move(x, n, z); // 将编号为n的圆盘由x移到z上
hanoi(n-1, y, x, z); // 将y上编号为1至n-1的圆盘移动x上,x做辅助塔座(降阶递归调用)
}
}
int main()
{
int n;
printf("3个塔座为x,y,z,圆盘最初在x上,借助y座移动z座。请输入圆盘数:");
scanf("%d", &n);
hanoi(n, 'x', 'y', 'z');
return 0;
}
5. 括号匹配
#include "ds.h"
typedef char SElemType;
#define STACK_INIT_SIZE 100
#define STACK_INCREMENT 10
typedef struct {
SElemType *base;
SElemType *top;
int stacksize;
}SqStack;
void InitStack(SqStack &S);
Status StackEmpty(SqStack S);
int StackLength(SqStack S);
Status GetTop(SqStack S, SElemType &e);
void Push(SqStack &S, SElemType e);
Status Pop(SqStack &S, SElemType &e);
void StackTraverse(SqStack S, void(* visit)(SElemType));
// 构造一个空栈S
void InitStack(SqStack &S)
{
S.base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType));
if (!S.base) exit(OVERFLOW);
S.top = S.base;
S.stacksize = STACK_INIT_SIZE;
}
// 若栈S为空栈,则返回TRUE,否则返回FALSE
Status StackEmpty(SqStack S)
{
if (S.top == S.base)
return TRUE;
else
return FALSE;
}
// 返回S的元素个数,即栈的长度
int StackLength(SqStack S)
{
return S.top - S.base; // not return S.stacksize;
}
// 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR
Status GetTop(SqStack S, SElemType &e)
{
if (S.top > S.base)
{
memcpy(&e, S.top - 1, sizeof(SElemType));
return OK;
}
else
{
return ERROR;
}
}
// 插入元素e为新的栈顶元素
void Push(SqStack &S, SElemType e)
{
if (S.top - S.base >= S.stacksize)
{
S.base = (SElemType*)realloc(S.base, (S.stacksize + STACK_INCREMENT) * sizeof(SElemType));
if (!S.base) exit(OVERFLOW);
S.top = S.base + S.stacksize;
S.stacksize += STACK_INCREMENT;
}
memcpy(S.top, &e, sizeof(SElemType));
S.top++;
}
// 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
Status Pop(SqStack &S, SElemType &e)
{
if (S.top == S.base)
return ERROR;
memcpy(&e, --S.top, sizeof(SElemType));
return OK;
}
// 从栈底到栈顶依次对栈中每个元素调用函数visit()
void StackTraverse(SqStack S, void(* visit)(SElemType))
{
SElemType *p = S.base;
while(p < S.top)
{
visit(*p++);
}
printf("\n");
}
void print(SElemType c)
{
printf("%d ",c);
}
// 检查括号是否匹配
void bracketcheck()
{
SqStack S;
SElemType str[80], *p, e;
InitStack(S);
printf("请输入带括号(()、[] 和 {})的表达式\n");
gets(str);
p = str;
while (*p)
{
switch (*p)
{
case '(':
case '[':
case '{':
Push(S, *p++);
break;
case ')':
case ']':
case '}':
if (!StackEmpty(S))
{
Pop(S, e);
if ( !(( '(' == e && ')'== *p )
||( '[' == e && ']'== *p )
||( '{' == e && '}'== *p ))
)
{
// 出现 3 种情况以外时
printf("NO 左右括号不配对\n");
exit(ERROR);
}
}
else // 栈空
{
printf("miss ( 缺乏左括号\n");
exit(ERROR);
}
default:
p++; //其他字符不做处理,指针后移
}
}
if (StackEmpty(S))
printf("yes 括号匹配\n");
else
printf("no 缺乏右括号\n");
}
int main()
{
bracketcheck();
}