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(); }