数据结构实现—栈实现三大表达式求值(人机交互友好,纯C)
本篇代码有详细注释,代码中有调试因子,get_s是vs中的gets加强版
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <math.h>
#include <ctype.h>
#include<string.h>
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define _OVERFLOW -2
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
#define MAXINPUT 1000
typedef int status;
typedef struct optrStack
{
char* base;
char* top;
int stacksize;
}optrStack;
typedef struct opndStack
{
int* base;
int* top;
int stacksize;
}opndStack;
void intro();
int menu();
status initTR(optrStack* stack);
status initND(opndStack* stack);
char getTopTR(optrStack stack);
int getTopND(opndStack stack);
status getInput(char* exp, int mode);
int isError(char* exp, int mode);
status pushTR(optrStack* stack, char e);
status pushND(opndStack* stack, int e);
status popTR(optrStack* stack, char* e);
status popND(opndStack* stack, int* e);
int isND(char c);
int isTR(char c);
int operate(int a, char theta, int b);
char precede(char a, char b);
status calcPolan();
status calcNPolan();
status calcMiddle();
int char2int(char c);
int str2int(char* s, int count, int digitCount);
int str2intTraverse(char* s, int count, int digitCount);
int collect()
{
char exp[100];
scanf("%s", exp);
printf("%s\n", exp);
}
int main()
{
// 介绍功能
intro();
// 定义变量
int select;
select = menu();
// 展示功能并获取选项
while (select != 4)
{
switch (select)
{
case 1:
calcPolan();
break;
case 2:
calcNPolan();
break;
case 3:
calcMiddle();
break;
default:
printf("谢谢使用,再见!\n");
break;
}
select = menu();
}
return 0;
}
void intro()
{
// 本函数用于展示程序一开始的页面,以及使用说明
printf(" 计算表达式运算器 \n");
printf("本运算器可以实现波兰式计算、逆波兰式计算、中缀表达式计算\n");
printf("\n\n");
}
int menu()
{
// 用于展示菜单
printf("\n请选择需要进行计算的表达式:\n");
printf("1\t波兰式\n");
printf("2\t逆波兰式\n");
printf("3\t中缀表达式\n");
printf("4\t退出系统\n");
printf("请输入您想做的功能的序号,并按回车键:\n");
// 获取输入
int select;
scanf("%d", &select);
rewind(stdin); // 回车会被传过去,造成下面的gets()函数挂掉,把缓冲区处理了
return select;
}
int isError(char* exp, int mode)
{
// 用于检验输入的字符串是否有问题
int result = 0;
int count = 0;
int left_bracket_count = 0;
int right_bracket_count = 0;
int tr_count = 0;
int nd_count = 0;
char c;
int digitCount = 0;
// 通用的情况
while (exp[count] != '\0')
{
// 验证是否为非法输入
char c = exp[count];
if (!isTR(c) && !isND(c) && c != ' ')
{
result = 1;
printf("存在非法字符,请重新输入\n");
break;
}
// 查看括号是否匹配——保证左括号在前,右括号在后
// printf("%d\t%c\n", count, c);
if (c == '(')
left_bracket_count++;
if (c == ')')
right_bracket_count++;
if (right_bracket_count > left_bracket_count)
{
result = 1;
printf("保证左括号在前,右括号在后,请重新输入\n");
break;
}
if (exp[count + 1] == ' ' || exp[count + 1] == '\0')
{
count++;
continue;
}
// 主要验证一下,同一个“段”内,是不是都是数字或者符号
while (exp[count + 1] != ' ' && exp[count + 1] != '\0')
{
// 如果下一个是空格,那就说明应该要断了
// 但是如果不是,那么就说明是有两位以上的数字
// 或者输入错误,即数字和运算符放一块了,就要重新输入
// 首先看是不是运算符,如果是,那肯定就是输入错了
if (isTR(exp[count]))
{
// 运算符只能单独成一段,后面还不是空格,必是错的
printf("运算符前后必须有空格,请重新输入:\n");
result = 1;
break;
}
if (isND(exp[count]))
{
// 如果第一个是数字,那么后面接的必须也是数字
if (!isND(exp[count + 1]))
{
result = 1;
printf("数字与字符不得在同一节内混输,请重新输入:\n");
break;
}
}
count++;
}
// 如果输入有问题,就跳出去准备重新输入
if (result)
{
break;
}
}
// 最后要看一下左右括号数量是否相等
if (left_bracket_count != right_bracket_count)
{
// printf("左:%d, 右%d", left_bracket_count,right_bracket_count);
result = 1;
printf("左右括号数量不相等,请重新输入:\n");
}
// 验证在(逆)波兰表达式里面是否会出现操作数和运算符数目不匹配的情况
if (mode == 1 || mode == 2)
{
// 开始操作
count = 0;
while (exp[count] != '\0')
{
c = exp[count];
// 统计ND和TR的数量,——直接把下面的拿来魔改
if (isND(c))
{
if (exp[count + 1] == ' ' || exp[count + 1] == '\0')
{
// 说明就是一位数字,直接入栈就行(入的int数字)
nd_count++;
count++;
}
else
{
// 但多位数字的情况,比如124就要占三个字符
// 所以要用个循环看一下
while (exp[count + 1] != ' ' && exp[count + 1] != '\0')
{
// 那说明是一个多位数字了
digitCount++;
count++;
}
nd_count++;
count++;
digitCount = 0;
}
}
else if (isTR(c))
{
// 如果是运算符
tr_count++;
count++;
}
else if (c == ' ')
{
// 取到了空格,走下一个
count++;
}
else
{
// 特殊情况
printf("有一个字符既不是操作数又不是操作符,自动跳过\n");
count++;
}
}
// 统计完毕后,如果数量相差不是1,那就说明有问题
if (nd_count - tr_count != 1)
{
result = 1;
printf("操作数和运算符数目不匹配,请重新输入:\n");
}
}
return result;
}
status getInput(char* exp, int mode)
{
// 用于获得用户输入,并做一些基础的验证处理
gets_s(exp, 999);
int x;
while (isError(exp, mode))
{
printf("退出输入请按0,其他键代表继续输入:\n");
scanf("%d", &x);
rewind(stdin);
if (x == 1)
{
printf("请重新输入:\n");
gets_s(exp, 999);
}
else
return !OK;
}
return OK;
}
int char2int(char c)
{
// 用于把单个字符转成数字
return c - 48;
}
int str2int(char* s, int count, int digitCount)
{
// 把多个字符转成一个多位数字
int result = 0;
int i, digit;
// printf("进入多位数字");
for (i = digitCount - 1; i >= 0; i--)
{
// printf("%c\n", s[count-i]);
digit = char2int(s[count - i]);
result += digit * pow(10, i);
// printf("result=%d\n", result);
}
return result;
}
int str2intTraverse(char* s, int count, int digitCount)
{
// 把多个字符变成一个多位数字的 逆序版本
int result = 0;
int i, digit;
for (i = 0; i < digitCount; i++)
{
// printf("%c\n", s[count-i]);
digit = char2int(s[count + i]);
result += digit * pow(10, digitCount - i - 1);
// printf("result=%d\n", result);
}
return result;
}
int isND(char c)
{
// 判断一个字符是不是操作数
// 其实直接用isdigit就行了,但是为了保持对称,还是用isND
return isdigit(c);
}
int isTR(char c)
{
// 判断一个字符是不是操作符
char trSet[] = { '+', '-','*','/','(',')','\0' };
int flag = 0;
for (int i = 0; i < 7; i++)
{
if (c == trSet[i])
{
flag = 1;
}
}
return flag;
}
int operate(int a, char theta, int b)
{
// 用于计算三元表达式结果
switch (theta)
{
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a * b;
case '/':
return a / b;
default:
return ERROR;
}
}
char precede(char a, char b)
{
// 判断两个字符的优先级
char big = '>';
char small = '<';
char same = '=';
switch (a)
{
case '+':
if (b == '*' || b == '/' || b == '(')
return small;
else
return big;
break;
case '-':
if (b == '*' || b == '/' || b == '(')
return small;
else
return big;
break;
case '*':
if (b == '(')
return small;
else
return big;
break;
case '/':
if (b == '(')
return small;
else
return big;
break;
case '(':
if (b == ')')
return same;
else
return small;
break;
case ')':
return big;
break;
case '\0':
if (b == '\0')
return same;
else
return small;
defult:
return same;
}
}
status calcPolan()
{
// 计算波兰式
// 获得用户输入,假设用户输入的字符小于MAXINPUT个
char PolanExp[MAXINPUT];
printf("\n请输入波兰式表达式,例如+ 2 * 3 - 5 1(中间需要有空格):\n");
if (!getInput(PolanExp, 1))
return OK;
printf("输入为:%s\n", PolanExp);
// 初始化栈
// 在这里只要用到一个数字栈
opndStack opnd;
initND(&opnd);
// 对所获得的字符串进行处理
int count = 0;
int digitCount = 1;
char x, theta;
int a, b;
// 为了方便后面操作,先统计一下长度(不含结尾的字符)
int length = 0;
while (PolanExp[count] != '\0')
{
length++;
count++;
}
for (count = length; count >= 0; )
{
// 这里的处理方式跟别的有些不同,因为是倒序处理的
// 所以这里的各种++就都要改为--
// 取出要处理的字符
char c = PolanExp[count];
// if(c!=' ')
// {
// printf("取出的字符为:%c\n", c);
// printf("是否为操作数:%d\n", isND(c));
// }
// 如果是操作数,入栈
if (isND(c))
{
// 由于这里可能会出现数组下溢的情况,所以就要先判断一下
if (count == 0)
{
// c是nd并且是最后一位,那肯定是一位数字,直接入栈
pushND(&opnd, char2int(c));
count--;
// printf("操作数入栈\n");
}
else
{
// 现在就可以来判断是否是多位数字了
if (PolanExp[count - 1] == ' ')
{
// 说明就是一位数字,直接入栈就行(入的int数字)
pushND(&opnd, char2int(c));
count--;
// printf("操作数入栈\n");
}
else
{
// 但多位数字的情况,比如124就要占三个字符
// 所以要用个循环看一下
while (PolanExp[count - 1] != ' ')
{
// 那说明是一个多位数字了
digitCount++;
count--;
}
int multiDigitInt = str2intTraverse(PolanExp, count, digitCount);
pushND(&opnd, multiDigitInt);
digitCount = 0;
count--;
}
}
}
else if (isTR(c))
{
// 如果是运算符进来了,直接进行运算
popND(&opnd, &a);
popND(&opnd, &b);
theta = c;
pushND(&opnd, operate(a, theta, b));
count--;
}
else if (c == ' ')
{
// 取到了空格,走下一个
count--;
}
else
{
// 特殊情况
printf("有一个字符既不是操作数又不是操作符,自动跳过\n");
count--;
}
}
printf("\n波兰式计算结果为:\n%s = %d\n\n", PolanExp, getTopND(opnd));
return OK;
}
status calcNPolan()
{
// 计算逆波兰式
// 获得用户输入,假设用户输入的字符小于MAXINPUT个
char NPolanExp[MAXINPUT];
printf("\n请输入逆波兰式表达式,例如2 3 5 1 - * +(中间需要有空格):\n");
if (!getInput(NPolanExp, 2))
return OK;
// printf("输入为:%s\n", NPolanExp);
// 初始化栈
// 在这里只要用到一个数字栈
opndStack opnd;
initND(&opnd);
// 对所获得的字符串进行处理
int count = 0;
int digitCount = 1;
char x, theta;
int a, b;
// 开始处理字符串——这里就不用考虑最后一个\0符号了
while (NPolanExp[count] != '\0')
{
// 取出要处理的字符
char c = NPolanExp[count];
// if(c!=' ')
// {
// printf("取出的字符为:%c\n", c);
// printf("是否为操作数:%d\n", isND(c));
// }
// 如果是操作数,入栈
if (isND(c))
{
if (NPolanExp[count + 1] == ' ' || NPolanExp[count + 1] == '\0')
{
// 说明就是一位数字,直接入栈就行(入的int数字)
pushND(&opnd, char2int(c));
count++;
// printf("操作数入栈\n");
}
else
{
// 但多位数字的情况,比如124就要占三个字符
// 所以要用个循环看一下
while (NPolanExp[count + 1] != ' ' && NPolanExp[count + 1] != '\0')
{
// 那说明是一个多位数字了
digitCount++;
count++;
}
int multiDigitInt = str2int(NPolanExp, count, digitCount);
pushND(&opnd, multiDigitInt);
count++;
digitCount = 0;
}
}
else if (isTR(c))
{
// 如果是运算符进来了,直接进行运算
popND(&opnd, &b);
popND(&opnd, &a);
theta = c;
pushND(&opnd, operate(a, theta, b));
count++;
}
else if (c == ' ')
{
// 取到了空格,走下一个
count++;
}
else
{
// 特殊情况
printf("有一个字符既不是操作数又不是操作符,自动跳过\n");
count++;
}
}
printf("\n逆波兰式计算结果为:\n%s = %d\n\n", NPolanExp, getTopND(opnd));
return OK;
}
status calcMiddle()
{
// 计算中缀表达式
// 获得用户输入,假设用户输入的字符小于MAXINPUT个
char middleExp[MAXINPUT];
printf("\n请输入中缀表达式,例如4 + 2 * 3 - 10 / 5(中间需要有空格):\n");
if (!getInput(middleExp, 3))
return OK;
// printf("输入为:%s\n", middleExp);
// 初始化栈
optrStack optr;
opndStack opnd;
initTR(&optr);
initND(&opnd);
// 在运算符栈里面放一个开始的标志
pushTR(&optr, '\0');
// 对所获得的字符串进行处理
int count = 0;
int digitCount = 1;
char x, theta;
int a, b;
// 为了方便后面操作,先统计一下长度(含结尾的\0字符)
int length = 1;
while (middleExp[count] != '\0')
{
length++;
count++;
}
// while(middleExp[count] != '\0')
for (count = 0; count < length; )
{
// 取出要处理的字符
char c = middleExp[count];
// if(c!=' ')
// {
// printf("取出的字符为:%c\n", c);
// printf("是否为操作数:%d\n", isND(c));
// }
// 如果是操作数,入栈
if (isND(c))
{
if (middleExp[count + 1] == ' ' || middleExp[count + 1] == '\0')
{
// 说明就是一位数字,直接入栈就行(入的int数字)
pushND(&opnd, char2int(c));
count++;
// printf("操作数入栈\n");
}
else
{
// 但多位数字的情况,比如124就要占三个字符
// 所以要用个循环看一下
while (middleExp[count + 1] != ' ' && middleExp[count + 1] != '\0')
{
// 那说明是一个多位数字了
digitCount++;
count++;
}
int multiDigitInt = str2int(middleExp, count, digitCount);
pushND(&opnd, multiDigitInt);
count++;
digitCount = 0;
}
}
else if (isTR(c))
{
// 如果是运算符,则要比较优先级
// printf("%c\n", getTopTR(optr));
// printf("%c\n", precede(getTopTR(optr),c));
switch (precede(getTopTR(optr), c))
{
case '<':
// 栈顶元素优先级低
pushTR(&optr, c);
count++;
break;
case '=':
// 脱括号并接收下一个字符
popTR(&optr, &x);
count++;
break;
case '>':
// printf("theta: %c\n", getTopTR(optr ));
// popTR(&optr, &theta);
// printf("theta: %c\n", theta);
// popND(&opnd, &b);
// printf("b: %d\n", b);
// popND(&opnd, &a);
// printf("a: %d\n", a);
// printf("result\n: %d", operate(a, theta, b));
// pushND(&opnd, operate(a, theta, b));
// printf("result2\n: %d", getTopND(opnd));
popTR(&optr, &theta);
popND(&opnd, &b);
popND(&opnd, &a);
pushND(&opnd, operate(a, theta, b));
}
}
else if (c == ' ')
{
// 取到了空格,走下一个
count++;
}
else
{
// 特殊情况
printf("有一个字符既不是操作数又不是操作符,自动跳过\n");
count++;
}
}
printf("\n中缀表达式计算结果为:\n%s = %d\n\n", middleExp, getTopND(opnd));
return OK;
}
status initTR(optrStack* stack)
{
// 初始化一个栈
stack->base = (char*)malloc(STACK_INIT_SIZE * sizeof(char));
if (!stack->base)
{
exit(_OVERFLOW);
}
stack->top = stack->base;
stack->stacksize = STACK_INIT_SIZE;
return OK;
}
status initND(opndStack* stack)
{
// 初始化一个栈
stack->base = (int*)malloc(STACK_INIT_SIZE * sizeof(int));
if (!stack->base)
{
exit(_OVERFLOW);
}
stack->top = stack->base;
stack->stacksize = STACK_INIT_SIZE;
return OK;
}
char getTopTR(optrStack stack)
{
// 取栈顶元素
if (stack.base == stack.top)
{
return ERROR;
}
else
{
return *(stack.top - 1);
}
}
int getTopND(opndStack stack)
{
// 取栈顶元素
if (stack.base == stack.top)
{
return ERROR;
}
else
{
return *(stack.top - 1);
}
}
status pushTR(optrStack* stack, char e)
{
// 插入元素e为新的栈顶元素
if (stack->top - stack->base >= stack->stacksize)
{
// 满了,分配新空间
stack->base = (char*)realloc(stack->base, (stack->stacksize + STACKINCREMENT) * sizeof(char));
if (!stack->base)
{
exit(_OVERFLOW);
}
stack->top = stack->base + stack->stacksize;
stack->stacksize += STACKINCREMENT;
}
*(stack->top++) = e;
return OK;
}
status pushND(opndStack* stack, int e)
{
// 插入元素e为新的栈顶元素
if (stack->top - stack->base >= stack->stacksize)
{
// 满了,分配新空间
stack->base = (int*)realloc(stack->base, (stack->stacksize + STACKINCREMENT) * sizeof(int));
if (!stack->base)
{
exit(_OVERFLOW);
}
stack->top = stack->base + stack->stacksize;
stack->stacksize += STACKINCREMENT;
}
*(stack->top++) = e;
return OK;
}
status popTR(optrStack* stack, char* e)
{
// 删除栈元素并用e带回
if (stack->top == stack->base)
{
return ERROR;
}
else
{
*e = *(--(stack->top));
return OK;
}
}
status popND(opndStack* stack, int* e)
{
// 删除栈元素并用e带回
if (stack->top == stack->base)
{
return ERROR;
}
else
{
*e = *(--(stack->top));
return OK;
}
}
//一些测试数据
/*
+ 2 * 3 - 5 1
+ + 2 * 3 - 7 4 / 8 4
2 3 5 1 - * +
9 3 1 – 3 * + 10 2 / +
4 + 2 * 3 – 10 / 5
( 4 + 2 ) * 3 – 10 / 5
+ 2 A
2 3 A -
4 + a
+ 2 2 2
2 2 2 +
2 + 2 2
+ + 2 2
2 2 + +
2 + + 2
( 4 + 2 * 3
4 + 2) * 3
*/