修改BUG的时候一不小心BUG越修越多,鉴于维护程序并不是学习数据结构的初衷,我已经果断的弃坑了!!
以下内容再不更新,Github上的代码直接无法正常编译运行。。。。
参考参考就好,学习到栈的作用就好。。。
===========================================================
使用数据结构中的栈以及链表来制作一个能够处理中缀表达式的计算程序。
在该程序中输入的数字可以是任意正数、小数。
负数似乎也可以正常运行,即使我没有特意添加检测负数的代码。。。。。
输入的运算符可以是 + - * / ( )
但请确保输入的表达式合法。
中缀表达式:
形如
9+(3-1)*3+10/2
的数学表达式。特点是运算符在被运算的两个数字中间,也就是我们日常接触的算式。
后缀表达式:
以上面的中缀表达式 9+(3-1)*3+10/2 为例
转换成后缀表达式为 9 3 1-3*+ 10 2/+
特点是运算符紧跟在被运算的两个数之后,这样方便程序的理解与计算。
只要顺序读取数字并根据其后的运算符进行相应的操作即可得到结果。
对于以上的转换如有不懂可参考
程序大致分为3步实现:
1)将输入的字符串拆分为数字与运算符,用一个链表储存它们,方便接下来的计算
2)使用仅储存运算符的栈将该中缀表达式转换为后缀表达式
3)使用仅储存数字的栈来运算后缀表达式得到最终答案
为实现这3步,代码有点繁琐。。。用了多个文件来分别实现具体功能
以下是各个代码及大概作用,由于比较长,全部折叠了。
Stack.h & Stack.c
实现了一个储存运算符的栈(用于进行第2步)和一个储存数字的栈(用于第3步)
1 ////////////// 2 // Stack.h // 3 ////////////// 4 5 #ifndef _STACK_H_ 6 #define _STACK_H_ 7 8 #include<stdlib.h> 9 10 #define STACK_INIT_SIZE 10 11 #define STACKINCREMENT 10 12 13 //优先级的定义 14 #define TopLow -1 15 #define Equal 0 16 #define TopHigh 1 17 //输入的是个括号 18 #define InputIsBrace_1 2 19 #define InputIsBrace_2 3 20 //当前栈顶是个括号 21 #define TopIsBrace 4 22 23 typedef char SymType; 24 typedef float NumType; 25 26 //第一步 27 //将中缀表达式转换为后缀表达式 28 //该栈用于储存符号 29 struct _Operator 30 { 31 SymType *Top,*Bottom; 32 int stacksize; 33 }; 34 typedef struct _Operator OPERATOR; 35 36 //新建栈 37 int InitStack_O(OPERATOR *S); 38 39 //销毁栈 40 int DestroyStack_O(OPERATOR *S); 41 42 //检测栈是否为空 43 //返回1表示为空 44 int StackEmpty_O(OPERATOR S); 45 46 //获取栈顶部元素 47 SymType GetTop_O(OPERATOR S); 48 49 //与栈顶的优先级进行比较 50 int TopIsPriority_O(OPERATOR S, SymType Input,SymType Top); 51 52 //Push操作,入栈,压栈 53 int Push_O(OPERATOR *S, SymType e); 54 55 //Pop操作,出栈 56 SymType Pop_O(OPERATOR *S); 57 58 //以上为符号栈 59 //===================================================================== 60 //以下为数字栈 61 62 //第二步 63 //计算后缀表达式的值 64 //用于储存数据 65 struct _Number 66 { 67 NumType *Top, *Bottom; 68 int stacksize; 69 }; 70 typedef struct _Number NUMBER; 71 72 //新建栈 73 int InitStack_N(NUMBER *S); 74 75 //销毁栈 76 int DestroyStack_N(NUMBER *S); 77 78 //获取栈顶部元素 79 NumType GetTop_N(NUMBER S); 80 81 //Push操作,入栈,压栈 82 int Push_N(NUMBER *S, NumType e); 83 84 //Pop操作,出栈 85 NumType Pop_N(NUMBER *S); 86 87 //接受运算符 88 //取栈顶的两个数字 89 //进行相应运算 90 void DoTheMath(NUMBER*,char); 91 92 #endif
1 ////////////// 2 // Stack.c // 3 ///////////// 4 5 #include"Stack.h" 6 7 int InitStack_O(OPERATOR *S) 8 { 9 //创建出设定长度的顺序表,地址赋给bottom指针 10 S->Bottom = (SymType*)malloc(STACK_INIT_SIZE*sizeof(SymType)); 11 if (!S->Bottom) 12 { 13 return 0; 14 } 15 S->stacksize = STACK_INIT_SIZE; 16 S->Top = S->Bottom; 17 return 1; 18 } 19 20 int DestroyStack_O(OPERATOR *S) 21 { 22 S->Top = NULL; 23 free(S->Bottom); 24 S->Bottom = NULL; 25 return 1; 26 } 27 28 int StackEmpty_O(OPERATOR S) 29 { 30 if (S.Bottom == S.Top) 31 { 32 return 1; 33 } 34 else 35 { 36 return 0; 37 } 38 } 39 40 SymType GetTop_O(OPERATOR S) 41 { 42 if (S.Bottom != S.Top) 43 { 44 //由于top指向的是最顶上元素的下一个位置 45 //所以取出最顶上元素的时候 46 //要把top减去1 47 return *(S.Top - 1); 48 } 49 return 0; 50 } 51 52 //与栈顶的优先级进行比较 53 int TopIsPriority_O(OPERATOR S, SymType Input,SymType top) 54 { 55 if (Input == '(') 56 { 57 return InputIsBrace_1; 58 } 59 if (Input == ')') 60 { 61 return InputIsBrace_2; 62 } 63 if (top == '(') 64 { 65 return TopIsBrace; 66 } 67 //-+优先级为1 68 //*/优先级为2 69 int p_top, p_input; 70 if (top == '-' || top == '+') 71 { 72 p_top = 1; 73 } 74 if (top == '*' || top == '/') 75 { 76 p_top = 2; 77 } 78 if (Input == '-' || Input == '+') 79 { 80 p_input = 1; 81 } 82 if (Input == '*' || Input == '/') 83 { 84 p_input = 2; 85 } 86 87 if (p_input > p_top) 88 { 89 return TopLow; 90 } 91 if (p_input == p_top) 92 { 93 return Equal; 94 } 95 if (p_input < p_top) 96 { 97 return TopHigh; 98 } 99 return 0; 100 } 101 102 int Push_O(OPERATOR *S, SymType e) 103 { 104 //当超出当前栈的容量时进行扩容 105 //这里应该只有等于的情况 106 //而不会大于 107 if ((S->Top - S->Bottom) >= S->stacksize) 108 { 109 //realloc函数将开辟指定大小的储存空间 110 //并将原来的数据全部移到这个新的储存空间 111 S->Bottom = (SymType*)realloc(S->Bottom, (S->stacksize + STACKINCREMENT)*sizeof(SymType)); 112 if (!S->Bottom) 113 { 114 return 0; 115 } 116 //由于重新开辟了空间 117 //需要重新根据bottom指针的位置指定top 118 S->Top = S->Bottom + S->stacksize; 119 //最后增加当前栈容量 120 S->stacksize += STACKINCREMENT; 121 } 122 //再把入栈的数据存放好 123 *(S->Top++) = e; 124 return 1; 125 } 126 127 SymType Pop_O(OPERATOR *S) 128 { 129 if (S->Bottom == S->Top) 130 { 131 return 0; 132 } 133 //top指针先减1再取值 134 return *(--S->Top); 135 } 136 137 //以上为符号栈的函数 138 //======================================================================== 139 //以下为数字栈的函数 140 141 int InitStack_N(NUMBER *S) 142 { 143 //创建出设定长度的顺序表,地址赋给bottom指针 144 S->Bottom = (NumType*)malloc(STACK_INIT_SIZE*sizeof(NumType)); 145 if (!S->Bottom) 146 { 147 return 0; 148 } 149 S->stacksize = STACK_INIT_SIZE; 150 S->Top = S->Bottom; 151 return 1; 152 } 153 154 int DestroyStack_N(NUMBER *S) 155 { 156 S->Top = NULL; 157 free(S->Bottom); 158 S->Bottom = NULL; 159 return 1; 160 } 161 162 NumType GetTop_N(NUMBER S) 163 { 164 if (S.Bottom != S.Top) 165 { 166 //由于top指向的是最顶上元素的下一个位置 167 //所以取出最顶上元素的时候 168 //要把top减去1 169 return *(S.Top - 1); 170 } 171 return 0; 172 } 173 174 int Push_N(NUMBER *S, NumType e) 175 { 176 //当超出当前栈的容量时进行扩容 177 //这里应该只有等于的情况 178 //而不会大于 179 if ((S->Top - S->Bottom) >= S->stacksize) 180 { 181 //realloc函数将开辟指定大小的储存空间 182 //并将原来的数据全部移到这个新的储存空间 183 S->Bottom = (NumType*)realloc(S->Bottom, (S->stacksize + STACKINCREMENT)*sizeof(NumType)); 184 if (!S->Bottom) 185 { 186 return 0; 187 } 188 //由于重新开辟了空间 189 //需要重新根据bottom指针的位置指定top 190 S->Top = S->Bottom + S->stacksize; 191 //最后增加当前栈容量 192 S->stacksize += STACKINCREMENT; 193 } 194 //再把入栈的数据存放好 195 *(S->Top++) = e; 196 return 1; 197 } 198 199 NumType Pop_N(NUMBER * S) 200 { 201 if (S->Bottom == S->Top) 202 { 203 return 0; 204 } 205 //top指针先减1再取值 206 return *(--S->Top); 207 } 208 209 void DoTheMath(NUMBER *S,char sym) 210 { 211 //从栈顶顺序读入两个数字 212 //进行运算 213 //并将结果压回栈内 214 float num1, num2; 215 num2 = Pop_N(S); 216 num1 = Pop_N(S); 217 switch (sym) 218 { 219 case '+': 220 Push_N(S, num1 + num2); 221 break; 222 case '-': 223 Push_N(S, num1 - num2); 224 break; 225 case '*': 226 Push_N(S, num1*num2); 227 break; 228 case '/': 229 Push_N(S, num1 / num2); 230 break; 231 default: 232 break; 233 } 234 }
StringToNumAndSym.h & StringToNumAndSym.c
这个文件的功能是。。。。
将输入的一大串字符串拆分成一个数字或一个运算符,并将他们按顺序储存到一个链表里。
用于进行第1步。
1 //////////////////////////////// 2 //StringToNumAndSym.h// 3 /////////////////////////////// 4 5 #ifndef _STRINGTONUMANDSYM_H_ 6 #define _STRINGTONUMANDSYM_H_ 7 8 #include<stdlib.h> 9 10 //用于储存输入的数字或者符号 11 //IsNum=1 将读取数字 12 //IsNum=0 将读取字符 13 struct data_node 14 { 15 int IsNum; 16 float Num; 17 char Sym; 18 struct data_node *next; 19 }; 20 typedef struct data_node EQUATION; 21 22 //该函数接受输入的字符串 23 //输出一串处理好的等式的指针 24 //用于表示数字或者运算符号加减乘除等等 25 void StringToEquation(char *,EQUATION**); 26 27 void DestroyEquation(EQUATION*); 28 29 #endif
1 ///////////////////////////////// 2 // StringToNumAndSym.c // 3 /////////////////////////////// 4 5 #include"StringToNumAndSym.h" 6 7 void StringToEquation(char *input, EQUATION **ptr_source) 8 { 9 EQUATION *ptr = NULL; 10 int lop; 11 //记录正数部分的temp 12 int Ptmp; 13 //记录小数部分的temp 14 float Dtmp; 15 //PNum记录正数的位数 16 //NNum记录小数的个数 17 float Dmulti; 18 int Pmulti; 19 20 for (lop = 0; input[lop] != 0; lop++) 21 { 22 //接下来有个数字 23 //除错,确保输入合法 24 if (input[lop] >= '0' && input[lop] <= '9' 25 || input[lop] == '+' || input[lop] == '-' || input[lop] == '/' || input[lop] == '*' 26 || input[lop] == '(' || input[lop] == ')' || input[lop] == '.') 27 { 28 if (input[lop] >= '0' && input[lop] <= '9') 29 { 30 Ptmp = 0; 31 Dtmp = 0; 32 Pmulti = 1; 33 Dmulti = 1; 34 35 //计算整数部分 36 for (; input[lop] >= '0' && input[lop] <= '9'; lop++) 37 { 38 Ptmp = Ptmp * 10 + (input[lop] - '0'); 39 } 40 41 //如果有小数 42 //计算小数部分 43 if (input[lop] == '.') 44 { 45 Dmulti = (float)0.1; 46 //计算出小数部分 47 for (lop += 1; input[lop] >= '0' && input[lop] <= '9'; lop++) 48 { 49 Dtmp += Dmulti*(float)(input[lop] - '0'); 50 Dmulti *= (float)0.1; 51 } 52 } 53 //得到数字了 54 //创建节点保存 55 if (ptr == NULL) 56 { 57 ptr = (EQUATION*)malloc(sizeof(EQUATION)); 58 //创建第一个节点之后 59 //由于传入的是指针的指针 60 //需要更改ptr_source的值 61 //保存头指针 62 *ptr_source = ptr; 63 } 64 else 65 { 66 ptr->next = (EQUATION*)malloc(sizeof(EQUATION)); 67 ptr = ptr->next; 68 } 69 ptr->next = NULL; 70 ptr->IsNum = 1; 71 ptr->Num = (float)Ptmp + Dtmp; 72 ptr->Sym = '0'; 73 74 lop--; 75 } 76 else 77 { 78 //第一个节点 79 if (ptr == NULL) 80 { 81 ptr = (EQUATION*)malloc(sizeof(EQUATION)); 82 *ptr_source = ptr; 83 } 84 //之后的节点 85 else 86 { 87 ptr->next = (EQUATION*)malloc(sizeof(EQUATION)); 88 ptr = ptr->next; 89 } 90 ptr->next = NULL; 91 ptr->IsNum = 0; 92 ptr->Num = 0; 93 ptr->Sym = input[lop]; 94 } 95 } 96 } 97 } 98 99 /* 100 //反转整数 101 int Invert(int input) 102 { 103 int output = 0; 104 while (input > 0) 105 { 106 output = output * 10 + input % 10; 107 input /= 10; 108 } 109 return output; 110 } 111 */ 112 113 void DestroyEquation(EQUATION *N) 114 { 115 EQUATION *temp = NULL; 116 if (N != NULL) 117 { 118 temp = N; 119 N = N->next; 120 free(temp); 121 temp = NULL; 122 123 DestroyEquation(N); 124 } 125 }
main.c
主函数在此,实现了判断运算符运算顺序的逻辑
1 ///////////// 2 // main.c // 3 ///////////// 4 5 #include<stdio.h> 6 7 #include"Stack.h" 8 #include"StringToNumAndSym.h" 9 10 int main() 11 { 12 int lop = 0; 13 14 //新建用于储存输入数据的字符串 15 char string[30] = { 0 }; 16 //新建用于储存处理后数据的字符串 17 EQUATION *Eqinput; 18 19 //以下4行创建并初始化两个栈 20 //分别用于存放运算符和数字 21 OPERATOR Stack_Ope; 22 NUMBER Stack_Num; 23 24 InitStack_O(&Stack_Ope); 25 InitStack_N(&Stack_Num); 26 27 //输入合法的中缀表达式 28 //不要太长 29 printf("请输入合法的中缀表达式(不带空格): "); 30 scanf("%s", string); 31 32 //将输入的字符串转化为能够处理的数据 33 StringToEquation(string, &Eqinput); 34 35 //将中缀表达式转换为后缀表达式 36 //同时包含对数据的运算 37 38 //如果输入的数据还没有处理完 39 while (Eqinput != NULL) 40 { 41 //看看当前对象是不是数字 42 if (Eqinput->IsNum == 1) 43 { 44 //是数字就直接丢到处理数字的栈里去 45 Push_N(&Stack_Num, Eqinput->Num); 46 } 47 else 48 { 49 if (StackEmpty_O(Stack_Ope)) 50 { 51 Push_O(&Stack_Ope, Eqinput->Sym); 52 } 53 54 //是运算符的话 55 //将他与上一个运算符,也就是栈顶的那个运算符进行比较 56 //然后根据两者的优先级大小来做出相应操作 57 else 58 { 59 //优先级判断 60 switch (TopIsPriority_O(Stack_Ope,Eqinput->Sym,GetTop_O(Stack_Ope))) 61 { 62 //如果栈顶是括号 63 //不进行优先级的比较 64 //直接入栈 65 case TopIsBrace: 66 Push_O(&Stack_Ope, Eqinput->Sym); 67 break; 68 69 //输入了左括号 70 case InputIsBrace_1: 71 Push_O(&Stack_Ope, '('); 72 break; 73 74 //输入了右括号 75 //一直出栈 76 //直到遇到左括号 77 case InputIsBrace_2: 78 while((GetTop_O(Stack_Ope) != '(')) 79 { 80 //printf("%c ", Pop_O(&Stack_Ope)); 81 DoTheMath(&Stack_Num, Pop_O(&Stack_Ope)); 82 } 83 Pop_O(&Stack_Ope); 84 break; 85 86 //当前运算符优先级比栈顶的低或者相等 87 case TopHigh: 88 case Equal: 89 while (!StackEmpty_O(Stack_Ope) ) 90 { 91 if (GetTop_O(Stack_Ope) == '(') 92 { 93 break; 94 } 95 //printf("%c ", Pop_O(&Stack_Ope)); 96 DoTheMath(&Stack_Num, Pop_O(&Stack_Ope)); 97 } 98 Push_O(&Stack_Ope, Eqinput->Sym); 99 break; 100 101 //当前运算符优先级比栈顶的高 102 case TopLow: 103 Push_O(&Stack_Ope, Eqinput->Sym); 104 break; 105 default: 106 break; 107 } 108 } 109 } 110 //读入下一个数据 111 Eqinput = Eqinput->next; 112 } 113 114 //如果栈里还有残留的运算符 115 //全部出栈 116 while (!StackEmpty_O(Stack_Ope)) 117 { 118 //printf("%c ", Pop_O(&Stack_Ope)); 119 DoTheMath(&Stack_Num, Pop_O(&Stack_Ope)); 120 } 121 122 printf(" "); 123 124 ////////////// 125 //运算完毕// 126 ///////////// 127 128 printf("%s = %.3f ", string, GetTop_N(Stack_Num)); 129 130 //销毁输入的式子 131 DestroyEquation(Eqinput); 132 Eqinput = NULL; 133 134 DestroyStack_N(&Stack_Num); 135 DestroyStack_O(&Stack_Ope); 136 137 return 0; 138 }
运行效果: