数据结构算法C语言实现(八)--- 3.2栈的应用举例:迷宫求解与表达式求值
一.简介
迷宫求解:类似图的DFS。具体的算法思路可以参考书上的50、51页,不过书上只说了粗略的算法,实现起来还是有很多细节需要注意。大多数只是给了个抽象的名字,甚至参数类型,返回值也没说的很清楚,所以很多需要自己揣摩。这也体现了算法和程序设计语言的特点,算法更侧重本质的描述,而任何编程语言都要照顾到实现的细节以及数据类型等语法方面的需求。
表达式求值:
由于数据的读入是按照字符读入的,所以这个简单的小程序只能计算个位数的运算。
二.头文件
迷宫求解:
1 //3_2_maze.h 2 /** 3 author:zhaoyu 4 email:zhaoyu1995.com@gmail.com 5 date:2016-6-8 6 note:realize my textbook <<数据结构(C语言版)>> 7 */ 8 //Page 51 9 #ifndef _3_2_MAZE_H 10 #define _3_2_MAZE_H 11 #include <cstdio> 12 #include <cstdlib> 13 #include <cstring> 14 #include "head.h" 15 #define STACK_INIT_SIZE 200//存储空间的初始分配值 16 #define STACKINCREMENT 10//存储空间分配增量 17 #define WALL -1 18 #define PATH 1 19 #define PASSED -2 20 #define UNREACHABLE -3 21 #define FINALPATH -4 22 #define NMAX 50 23 char Map[NMAX][NMAX]; 24 typedef struct{ 25 int x; 26 int y; 27 }PosType; 28 typedef struct node_1{ 29 int x, y; 30 struct node_1 *next; 31 }MazeType; 32 typedef struct{ 33 int ord;//通道块在路径上的序号 34 PosType seat;//通道块在迷宫中的位置 35 int direction;//从此通道块走向下一通道块的方向 36 }SElemType; 37 typedef struct{ 38 SElemType *base;//在栈构造之前和销毁之后,base 值为 NULL 39 SElemType *top;//栈顶指针 40 int stacksize;//当前已分配的存储空间,以元素为单位 41 }SqStack; 42 Status InitStack(SqStack &S) 43 { 44 //构造一个空栈 S 45 S.base = (SElemType *)malloc(STACK_INIT_SIZE*sizeof(SElemType)); 46 if (!S.base) 47 { 48 exit(OVERFLOW); 49 } 50 S.top = S.base; 51 S.stacksize = STACK_INIT_SIZE; 52 return OK; 53 }//InitStack 54 void Assign_PosType(PosType &a, PosType b) 55 { 56 a.x = b.x; 57 a.y = b.y; 58 }//Assign_PosType 59 Status Pass(PosType pos) 60 {//可能写的不对,注意 61 if (PATH == Map[pos.x][pos.y]) 62 { 63 return TRUE; 64 } 65 else 66 { 67 return FALSE; 68 } 69 }//Pass 70 void FoorPrint(PosType pos) 71 { 72 Map[pos.x][pos.y] = PASSED; 73 } 74 void Assign_SELemType(SElemType &e, int x, PosType pos, int y) 75 { 76 e.ord = x; 77 Assign_PosType(e.seat, pos); 78 e.direction = y; 79 } 80 Status Push(SqStack &S, SElemType e) 81 { 82 //插入元素 e 为新的栈顶元素 83 if (S.top - S.base >= S.stacksize) 84 {//栈满,追加存储空间 85 S.base = (SElemType *)realloc(S.base, 86 (S.stacksize+STACKINCREMENT)*sizeof(SElemType)); 87 if (!S.base) 88 { 89 exit(OVERFLOW); 90 } 91 S.top = S.base + S.stacksize; 92 S.stacksize += STACKINCREMENT; 93 } 94 //*S.top++ = e; 95 Assign_SELemType(*S.top++, e.ord, e.seat, e.direction); 96 return OK; 97 }//Push 98 PosType NextPos(PosType pos, int direction) 99 { 100 PosType temp; 101 switch (direction) 102 { 103 case 1: 104 { 105 temp.x = pos.x; 106 temp.y = pos.y+1; 107 break; 108 } 109 case 2: 110 { 111 temp.x = pos.x + 1; 112 temp.y = pos.y; 113 break; 114 } 115 case 3: 116 { 117 temp.x = pos.x; 118 temp.y = pos.y - 1; 119 break; 120 } 121 case 4: 122 { 123 temp.x = pos.x - 1; 124 temp.y = pos.y; 125 break; 126 } 127 } 128 //加一个越界检查 129 return temp; 130 } 131 Status StackEmpty(SqStack S) 132 { 133 //若 S 为空栈, 则返回 TRUE, 否则返回 FALSE 134 if (S.base == S.top) 135 { 136 return TRUE; 137 } 138 else 139 { 140 return FALSE; 141 } 142 } 143 Status Pop(SqStack &S, SElemType &e) 144 { 145 //若栈不空,则删除 S 的栈顶元素,用 e 返回其 146 //值,并返回OK;否则返回ERROR 147 if (S.top == S.base) 148 { 149 return ERROR; 150 } 151 //e = *--S.top; 152 S.top--; 153 Assign_SELemType(e, (*S.top).ord, (*S.top).seat, (*S.top).direction); 154 return OK; 155 }//Pop 156 void FootPrint(PosType pos) 157 { 158 Map[pos.x][pos.y] = PASSED; 159 } 160 void MarkPrint(PosType pos) 161 { 162 Map[pos.x][pos.y] = UNREACHABLE; 163 } 164 void MakeMap(int size) 165 { 166 memset(Map, 0, sizeof(Map)); 167 char ch; 168 getchar(); 169 for (int i = 1; i <= size; i++) 170 { 171 for (int j = 1; j <= size; j++) 172 { 173 scanf("%c", &ch); 174 if ('X' == ch) 175 { 176 Map[i][j] = UNREACHABLE; 177 } 178 else 179 { 180 Map[i][j] = PATH; 181 } 182 } 183 //attention '\n' 184 getchar(); 185 } 186 //Print maze 187 for (int i = 1; i <= size; i++) 188 { 189 for (int j = 1; j <= size; j++) 190 { 191 if (UNREACHABLE == Map[i][j]) 192 { 193 printf("X"); 194 } 195 else 196 { 197 printf(" "); 198 } 199 } 200 printf("\n"); 201 } 202 } 203 void PrintPath(SqStack S, int size) 204 { 205 SElemType e; 206 SqStack temp; 207 while (!StackEmpty(S)) 208 { 209 Pop(S, e); 210 Map[e.seat.x][e.seat.y] = FINALPATH; 211 } 212 for (int i = 1; i <= size; i++) 213 { 214 for (int j = 1; j <= size; j++) 215 { 216 if (UNREACHABLE == Map[i][j]) 217 { 218 printf("X"); 219 } 220 else if (FINALPATH == Map[i][j]) 221 { 222 printf("O"); 223 } 224 else 225 { 226 printf(" "); 227 } 228 } 229 printf("\n"); 230 } 231 } 232 Status MazePath(MazeType maze, PosType start, PosType end, int size) 233 { 234 //若迷宫 maze 中存在从入口 start 到出口 end 的通道, 235 //则求得一条存放在栈中(从栈底到栈顶),并返回 TRUE, 236 //否则返回 FALSE 237 SqStack S; 238 InitStack(S); 239 //curpos = start 240 PosType curpos; 241 Assign_PosType(curpos, start);//设定当前位置为入口位置 242 int curstep = 1;//探索第一步 243 SElemType e; 244 do{ 245 if (TRUE == Pass(curpos)) 246 {//当前位置可以通过 247 FootPrint(curpos);//留下足迹 248 //e = (curstep, curpos, 1); 249 Assign_SELemType(e, curstep, curpos, 1); 250 Push(S, e);//加入路径 251 if (curpos.x == end.x && curpos.y == end.y) 252 { 253 //打印路径 254 printf("PATH EXIST\n"); 255 PrintPath(S ,size); 256 return TRUE; 257 } 258 curpos = NextPos(curpos, 1);//下一位置是当前位置的东邻 259 curstep++;//探索下一步 260 } 261 else 262 { 263 if (!StackEmpty(S)) 264 { 265 Pop(S, e); 266 while (4 == e.direction && !StackEmpty(S)) 267 { 268 MarkPrint(e.seat); 269 Pop(S, e);//留下不能通过的标记,并退回一步 270 } 271 if (e.direction < 4) 272 { 273 e.direction++; 274 Push(S, e); 275 curpos = NextPos(e.seat, e.direction); 276 } 277 } 278 } 279 }while (!StackEmpty(S)); 280 281 } 282 #endif
表达式求值:
1 //3_2_maze.h 2 /** 3 author:zhaoyu 4 email:zhaoyu1995.com@gmail.com 5 date:2016-6-8 6 note:realize my textbook <<数据结构(C语言版)>> 7 */ 8 //Page 53 9 #ifndef _3_2_EXPRESSION_H_ 10 #define _3_2_EXPRESSION_H_ 11 #include "head.h" 12 13 #define SElemType char 14 #define OperandType float 15 #define STACK_INIT_SIZE 100//存储空间的初始分配值 16 #define STACKINCREMENT 10//存储空间分配增量 17 typedef struct{ 18 SElemType *base;//在栈构造之前和销毁之后,base 值为 NULL 19 SElemType *top;//栈顶指针 20 int stacksize;//当前已分配的存储空间,以元素为单位 21 }SqStack_Char; 22 typedef struct{ 23 OperandType *base;//在栈构造之前和销毁之后,base 值为 NULL 24 OperandType *top;//栈顶指针 25 int stacksize;//当前已分配的存储空间,以元素为单位 26 }SqStack_Float; 27 Status InitStack_Char(SqStack_Char &S) 28 { 29 //构造一个空栈 S 30 S.base = (SElemType *)malloc(STACK_INIT_SIZE*sizeof(SElemType)); 31 if (!S.base) 32 { 33 exit(OVERFLOW); 34 } 35 S.top = S.base; 36 S.stacksize = STACK_INIT_SIZE; 37 return OK; 38 }//InitStack 39 Status InitStack_Float(SqStack_Float &S) 40 { 41 //构造一个空栈 S 42 S.base = (OperandType *)malloc(STACK_INIT_SIZE*sizeof(OperandType)); 43 if (!S.base) 44 { 45 exit(OVERFLOW); 46 } 47 S.top = S.base; 48 S.stacksize = STACK_INIT_SIZE; 49 return OK; 50 }//InitStack 51 Status Push_Char(SqStack_Char &S, SElemType e) 52 { 53 //插入元素 e 为新的栈顶元素 54 if (S.top - S.base >= S.stacksize) 55 {//栈满,追加存储空间 56 S.base = (SElemType *)realloc(S.base, 57 (S.stacksize+STACKINCREMENT)*sizeof(SElemType)); 58 if (!S.base) 59 { 60 exit(OVERFLOW); 61 } 62 S.top = S.base + S.stacksize; 63 S.stacksize += STACKINCREMENT; 64 } 65 *S.top++ = e; 66 return OK; 67 }//Push 68 SElemType GetTop_Char(SqStack_Char S) 69 { 70 //若栈不空,则用 e 返回 S 的栈顶元素,并返回 OK; 71 //否则返回ERROR 72 if (S.top == S.base) 73 { 74 return ERROR; 75 } 76 return *(S.top - 1); 77 }//GetTop 78 Status isInOPTR(SElemType c) 79 { 80 switch (c) 81 { 82 case '+': 83 case '-': 84 case '*': 85 case '/': 86 case '(': 87 case ')': 88 case '#': 89 { 90 return TRUE; 91 break; 92 } 93 default: 94 { 95 return FALSE; 96 break; 97 } 98 } 99 } 100 Status Push_Float(SqStack_Float &S, OperandType e) 101 { 102 //插入元素 e 为新的栈顶元素 103 if (S.top - S.base >= S.stacksize) 104 {//栈满,追加存储空间 105 S.base = (OperandType *)realloc(S.base, 106 (S.stacksize+STACKINCREMENT)*sizeof(OperandType)); 107 if (!S.base) 108 { 109 exit(OVERFLOW); 110 } 111 S.top = S.base + S.stacksize; 112 S.stacksize += STACKINCREMENT; 113 } 114 *S.top++ = e; 115 return OK; 116 }//Push 117 char Precede(SElemType a, SElemType b) 118 { 119 char R[7][7] = {{'>','>','<','<','<','>','>'}, 120 {'>','>','<','<','<','>','>'}, 121 {'>','>','>','>','<','>','>'}, 122 {'>','>','>','>','<','>','>'}, 123 {'<','<','<','<','<','=','#'}, 124 {'>','>','>','>','#','>','>'}, 125 {'<','<','<','<','<','#','='}}; 126 int i,j; 127 switch (a) 128 { 129 case '+':i=0;break; 130 case '-':i=1;break; 131 case '*':i=2;break; 132 case '/':i=3;break; 133 case '(':i=4;break; 134 case ')':i=5;break; 135 case '#':i=6;break; 136 default:i=0;break; 137 } 138 switch (b) 139 { 140 case '+':j=0;break; 141 case '-':j=1;break; 142 case '*':j=2;break; 143 case '/':j=3;break; 144 case '(':j=4;break; 145 case ')':j=5;break; 146 case '#':j=6;break; 147 default:j=0;break; 148 } 149 if ('#' == R[i][j]) 150 { 151 printf("ERROR\n"); 152 exit(ERROR); 153 } 154 else 155 { 156 return R[i][j]; 157 } 158 } 159 Status Pop_Char(SqStack_Char &S, SElemType &e) 160 { 161 //若栈不空,则删除 S 的栈顶元素,用 e 返回其 162 //值,并返回OK;否则返回ERROR 163 if (S.top == S.base) 164 { 165 return ERROR; 166 } 167 e = *--S.top; 168 return OK; 169 }//Pop 170 Status Pop_Float(SqStack_Float &S, OperandType &e) 171 { 172 //若栈不空,则删除 S 的栈顶元素,用 e 返回其 173 //值,并返回OK;否则返回ERROR 174 if (S.top == S.base) 175 { 176 return ERROR; 177 } 178 e = *--S.top; 179 return OK; 180 }//Pop 181 OperandType Operate(OperandType a, SElemType theta, OperandType b) 182 { 183 switch (theta) 184 { 185 case '+': return a+b; break; 186 case '-': return a-b; break; 187 case '*': return a*b; break; 188 case '/': return a/b; break; 189 default:return 0; break; 190 } 191 } 192 OperandType GetTop_Float(SqStack_Float S) 193 { 194 //若栈不空,则用 e 返回 S 的栈顶元素,并返回 OK; 195 //否则返回ERROR 196 if (S.top == S.base) 197 { 198 return ERROR; 199 } 200 return *(S.top - 1); 201 }//GetTop 202 OperandType EvaluateExpression() 203 { 204 //算数表达式求值的算符优先算法。设 OPTR 和 OPND 205 //分别为运算符栈和运算数栈,OP 为运算符集合 206 SqStack_Char OPTR; 207 SqStack_Float OPND; 208 InitStack_Char(OPTR); 209 Push_Char(OPTR, '#'); 210 InitStack_Float(OPND); 211 char c = getchar(), x, theta; 212 float a, b; 213 while ('#' != c || GetTop_Char(OPTR) != '#') 214 { 215 //if (!In(c, OP)) 216 if (!isInOPTR(c)) 217 { 218 float temp = c-'0'; 219 Push_Float(OPND, temp);//不是运算符则进栈 220 c = getchar(); 221 } 222 else 223 { 224 switch (Precede(GetTop_Char(OPTR), c)) 225 { 226 case '<'://栈顶元素优先权低 227 { 228 Push_Char(OPTR, c); 229 c = getchar(); 230 break; 231 } 232 case '='://脱括号并接收下一字符 233 { 234 Pop_Char(OPTR, x); 235 c = getchar(); 236 break; 237 } 238 case '>'://退栈并将运算符结果入栈 239 { 240 Pop_Char(OPTR, theta); 241 Pop_Float(OPND, b); 242 Pop_Float(OPND, a); 243 Push_Float(OPND, Operate(a, theta, b)); 244 break; 245 } 246 } 247 } 248 } 249 return GetTop_Float(OPND); 250 } 251 #endif
三.CPP文件
迷宫求解:
1 #include "3_2_maze.h" 2 int main(int argc, char const *argv[]) 3 { 4 freopen("map.txt", "r", stdin); 5 printf("Map\n"); 6 int size; 7 scanf("%d", &size); 8 MakeMap(size); 9 PosType start, end; 10 scanf("%d%d", &start.x, &start.y); 11 printf("start:\tx:%d\ty:%d\n", start.x, start.y); 12 scanf("%d%d", &end.x, &end.y); 13 printf("end:\tx:%d\ty:%d\n", end.x, end.y); 14 MazeType maze;//un useded for me 15 MazePath(maze, start, end, size); 16 //PrintPath(size); 17 return 0; 18 }
表达式求值:
1 #include "3_2_expression.h" 2 int main(int argc, char const *argv[]) 3 { 4 float ans; 5 for (int i = 0; i < 5; ++i) 6 { 7 ans = EvaluateExpression(); 8 printf("%.3f\n", ans); 9 } 10 return 0; 11 }
四.测试
迷宫求解:
表达式求值:
五.迷宫求解的输入文件map.txt
10 XXXXXXXXXX XOOXOOOXOX XOOXOOOXOX XOOOOXXOOX XOXXXOOOOX XOOOXOOOOX XOXOOOXOOX XOXXXOXXOX XXOOOOOOOX XXXXXXXXXX 2 2 9 9
六.优化的表达式求值代码
用了一些简单的C++语法(主要是为了节省代码),写了一个可以计算较复杂表达式的程序。(STL不熟,估计以后看会很蹩脚!)
1 #include <iostream> 2 #include <string> 3 #include <stack> 4 #include <cstdio> 5 #include <cstdlib> 6 #include <map> 7 using namespace std; 8 bool isInOPTR(char ch){ 9 switch (ch) { 10 case '+': 11 case '-': 12 case '*': 13 case '/': 14 case '(': 15 case ')': 16 case '#':{ 17 return true; 18 break; 19 } 20 default:{ 21 return false; 22 break; 23 } 24 } 25 } 26 char Precede(char a, char b){ 27 map<char, int> myMap; 28 myMap['+'] = 0; 29 myMap['-'] = 1; 30 myMap['*'] = 2; 31 myMap['/'] = 3; 32 myMap['('] = 4; 33 myMap[')'] = 5; 34 myMap['#'] = 6; 35 char R[7][7] = {{'>','>','<','<','<','>','>'}, 36 {'>','>','<','<','<','>','>'}, 37 {'>','>','>','>','<','>','>'}, 38 {'>','>','>','>','<','>','>'}, 39 {'<','<','<','<','<','=','#'}, 40 {'>','>','>','>','#','>','>'}, 41 {'<','<','<','<','<','#','='}}; 42 if ('#' == R[myMap[a]][myMap[b]]){ 43 printf("Precede ERROE\n"); 44 printf("%c%c\n", a, b); 45 exit(-1); 46 } 47 return R[myMap[a]][myMap[b]]; 48 } 49 float Caculate(float a, char theta, float b){ 50 switch (theta){ 51 case '+':{ 52 return a+b; 53 break; 54 } 55 case '-':{ 56 return a-b; 57 break; 58 } 59 case '*':{ 60 return a*b; 61 break; 62 } 63 case '/':{ 64 return a/b; 65 } 66 default:{ 67 printf("Operator ERROR\n"); 68 exit(-1); 69 } 70 } 71 } 72 void expression(void){ 73 string Str; 74 getline(cin, Str); 75 cout <<"Your input: " + Str + '\b' + ' ' << endl; 76 char Operator = ' '; 77 float Operand = 0; 78 stack<char> OPTR; 79 stack<float> OPND; 80 OPTR.push('#'); 81 int i = 0; 82 char number[20]; 83 while (Operator != '#' || OPTR.top() != '#'){ 84 //fool code!!! 85 for (int k = 0;i <= Str.length();){ 86 if (isInOPTR(Str.at(i))) 87 { 88 Operator = Str.at(i++); 89 break; 90 } 91 else 92 { 93 number[k] = Str.at(i); 94 k++; 95 i++; 96 if (isInOPTR(Str.at(i))) 97 { 98 number[k] = '\0'; 99 Operand = atof(number); 100 OPND.push(Operand); 101 continue; 102 } 103 104 } 105 } 106 switch (Precede(OPTR.top(), Operator)){ 107 case '<':{ 108 OPTR.push(Operator); 109 break; 110 } 111 case '=':{ 112 OPTR.pop(); 113 break; 114 } 115 case '>':{ 116 float a, b; 117 char theta; 118 theta = OPTR.top(); 119 OPTR.pop(); 120 b = OPND.top(); 121 OPND.pop(); 122 a = OPND.top(); 123 OPND.pop(); 124 OPND.push(Caculate(a, theta, b)); 125 i--;//attention!!!!!!! 126 break; 127 } 128 } 129 } 130 cout << "Answer: " <<OPND.top() << endl; 131 132 } 133 int main(int argc, char const *argv[]) 134 { 135 freopen("in.txt", "r", stdin); 136 freopen("out.txt", "w", stdout); 137 for (int i = 0; i < 5; ++i) 138 { 139 expression(); 140 } 141 return 0; 142 }
测试数据:
3.2*9-3/4+2# 3.45*100-(10-4)*5# 3*3/3/3# 2.22*(10-110)# 1-1#
结果:
Your input: 3.2*9-3/4+2 Answer: 30.05 Your input: 3.45*100-(10-4)*5 Answer: 315 Your input: 3*3/3/3 Answer: 1 Your input: 2.22*(10-110) Answer: -222 Your input: 1-1 Answer: 0