基于堆栈的计算器软件设计,需要控制数字及其符号有规律的进出栈最后对输出的表达式进行求值。
首先对堆栈进行介绍:
栈作为一种限定性线性表,是将线性表的插入和删除运算限制为仅在表的一端运行,通常将表中允许进行插入,删除操作的一端称为栈顶(top),因此栈顶的当前位置是动态变化的,它由一个称为栈顶指针的位置指示器指示。同时,表的另一端称为栈底(bottom),当栈中没有元素时称为空栈。栈的插入操作被形象的称为进栈或入栈,删除操作称为出栈或退栈。在设计过程中会反复用到进栈和出栈
一.步骤方法
1.先将表达式转化为后缀表达式,方法如下:
设置一个栈,开始时栈为空,自左向右扫描字符串遇到字符串中的数字时直接输出。遇到操作符时,则将当前运算符的优先级数与运算符栈顶元素的优先级数相比较。若当前运算符的优先级数大,则进栈;反之,则弹出栈顶的元素,然后输出一个空格作为分隔符号;若遇到左括号,进栈;遇见右括号。则一直退栈输出,知道遇见对应的左括号为止,当栈为空时,输出结果即为后缀表达式。
图解:如输出 9+(3-1)*3+10/2 栈的变化为
在这里面需要注意的是要将运算符进行比较,才能准确的进出栈。
代码如下:
int TransmitExpression(char *inorder, char *postorder)
{
Stack s;
int e = 0; 进行出栈入栈操作
int i = 0, j = 0; 分别进行循环数组的下标,i括为中序下标,j括为后序下标括。
int flag = 0;
if (CreateStack(&s) != 1) 判断栈是否为空
return 0;
while (inorder[i] != '\0') 说明栈中有元素。
{
while (inorder[i] >= '0' && inorder[i] <= '9') 如果是数字则输出 {
if (flag) 考虑负数的情况
{
postorder[j++] = '-';
}
postorder[j++] = inorder[i]; 让存放后缀表达式的?数组存放字符
i++;
if (inorder[i]<'0' || inorder[i]>'9') 判断是否为操作符,如果是,让后缀表括达式中存放一个' '
postorder[j++] = ' ';
}
if (inorder[i] == ')' ) 如果是关于括号的符号,则进行出栈操作 {
pop(&s, &e);
while (e != '(' )
{
postorder[j++] = e;
postorder[j++] = ' ';
pop(&s, &e);
}
}
else if (inorder[i] == '+' || inorder[i] == '-') 对于同运算级的+和í操作
{
if (inorder[i] == '-' && (i == 0 || (i != 0 && (inorder[i - 1]<'0' || inorder[i - 1]>'9')))) 当“-”号处于第一位,或前面是符号时,为负号标括志
flag = 1;
else if (stack_empty(s)) 如果栈空
push(&s, inorder[i]);
else
{
do
{
pop(&s, &e);
if (e == '(' ) 优先级最大,比较以后入栈
push(&s, e);
else
{
postorder[j++] = e; 进行后缀表达式的添加
postorder[j++] = ' '; 最后添加‘ ’字符分隔开
}
} while (!stack_empty(s) && e != '(' );
push(&s, inorder[i]);
}
}
else if (inorder[i] == '*' || inorder[i] == '/' || inorder[i] == '(' )对于乘除以及左括号的进行入栈
push(&s, inorder[i]);
else if (inorder[i] == NULL) 如果中序当前读取位为空
break;
else
return 0;
i++;
}
2.后缀表达式求值:从左边到右边扫描后缀表达式,若遇见操作数,则进栈;若遇见运算符号,则从栈中推出两个元素,先推出的放到运算符的右边,后退出的放到运算符号的左边。运算后的结果再进栈,直到后缀表达式扫描完毕。此时栈中仅有一个元素,即为运算的结果。
具体实现过程:设置一个栈,开始时,栈为空,然后从左到右扫描后缀表达式,若遇操作数,则进栈;若遇运算符,则从栈中退出两个元素,先退出的放到运算符的右边,后退出的 放到运算符左边,运算后的结果再进栈,直到后缀表达式扫描完毕。此时,栈中仅有一个元素,即为运算的结果。
图解求值栈的变化过程:例如输出后缀表达式:1 2 + 8 2 - 7 4 - / *
最后输出退栈输出6
具体代码为:
int EvaluateExpression(char *postorder, int *result)
{
Stack s;
char *op; //存放后缀表达式中的每个因数或运算符
char *buf = postorder; //声明buf和saveptr两个变量,是strtok_r函数的需要。
char *saveptr = NULL;
int d, e, f;
if (CreateStack(&s) != 1)
return 0;
while ((op = strtok(buf, " ")) != NULL)//利用字符串分割函数
{
buf = NULL; //把指针置空
switch (op[0])
{
case '+':
pop(&s, &d);
pop(&s, &e);
f = d + e;
push(&s, f);
break;
case '-':
if (op[1] >= '0' && op[1] <= '9')
{
d = atoi(op);
push(&s, d);
break;
}
pop(&s, &d);
pop(&s, &e);
f = e - d;
push(&s, f);
break;
case '*':
pop(&s, &d);
pop(&s, &e);
f = e*d;
push(&s, f);
break;
case '/':
pop(&s, &d);
pop(&s, &e);
f = e / d;
push(&s, f);
break;
default: //考虑数字的情况,进行atoi函数进行转化
d = atoi(op);
push(&s, d); //进行压栈
break;
}
}
pop(&s, result);
return 0;
}
实现这两个功能后基本就完成了百分之八十,最后需要的是对界面就行修饰和拓展。
结果展示为:
总体源程序为:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXSIZE 100
typedef double DataType;
//定义一个顺序存储栈
typedef struct
{
DataType data[MAXSIZE];
int top;//栈顶指针
}Stack;//栈名
/*******************基本算法********************************/
int CreateStack(Stack *s)//栈顶指针初始化,建立一个空栈。
{
s->top = -1;
return 1;
}
int stack_empty(Stack s)//判断栈是否为空。
{
if (s.top == -1)
return 1;
else
return 0;
}
int push(Stack *s, DataType x)//入栈
{
if (s->top == MAXSIZE - 1)//栈满
return 0;
else s->top++;
s->data[s->top] = x;//将x推入栈
return 1;
}
int pop(Stack *s, DataType *x)//出栈
{
if (s->top == -1)//栈空
return 0;
else *x = s->data[s->top];//栈顶元素弹出赋予x
s->top--;
return 1;
}
int quit()
{
exit(0);
return 0;
}
/*******************输入中缀表达式********************************/
int CreateExpression(char *inorder)//输入中缀表达式
{
printf("请输入一个中缀表达式:\n");
scanf("%s", inorder);
return 0;
}
/*******************中序表达式转换为后序表达式********************************/
int TransmitExpression(char *inorder, char *postorder)//
{
Stack s;
double e = 0;//进行出栈入栈操作
int i = 0, j = 0;//分别进行循环数组的下标,i为中序下标,j为后序下标。
int flag = 0;
if (CreateStack(&s) != 1) //判断栈是否为空,
return 0;
while (inorder[i] != '\0') //说明栈中有元素。
{
while (inorder[i] >= '0' && inorder[i] <= '9') //如果是数字则输出
{
if (flag) //考虑负数的情况
{
postorder[j++] = '-';
}
postorder[j++] = inorder[i];//让存放后缀表达式的数组存放字符
i++;
if (inorder[i]<'0' || inorder[i]>'9') //判断是否为操作符,如果是,让后缀表达式中存放一个' '
postorder[j++] = ' ';
}
if (inorder[i] == ')' ) //如果是关于括号的符号,则进行出栈操作
{
pop(&s, &e);
while (e != '(' )
{
postorder[j++] = e;
postorder[j++] = ' ';
pop(&s, &e);
}
}
else if (inorder[i] == '+' || inorder[i] == '-') //对于同运算级的+和-操作
{
if (inorder[i] == '-' && (i == 0 || (i != 0 && (inorder[i - 1]<'0' || inorder[i - 1]>'9')))) //当'-'号处于第一位,或前面是符号时,为负号标志
flag = 1;
else if (stack_empty(s))//如果栈空
push(&s, inorder[i]);
else
{
do
{
pop(&s, &e);
if (e == '(' ) //优先级最大,比较以后入栈
push(&s, e);
else
{
postorder[j++] = e; //进行后缀表达式的添加
postorder[j++] = ' '; //最后添加‘ ’字符分隔开
}
} while (!stack_empty(s) && e != '(' );
push(&s, inorder[i]);
}
}
else if (inorder[i] == '*' || inorder[i] == '/' || inorder[i] == '(' )//对于乘除以及左括号的进行入栈
push(&s, inorder[i]);
else if (inorder[i] == NULL)//如果中序当前读取位为空
break;
else
return 0;
i++;
}
while (!stack_empty(s))//栈非空
{
pop(&s, &e);
postorder[j++] = e;
postorder[j++] = ' ';
}
//clear_stack(&s);
return 1;
}
/*******************根据后续表达式计算结果********************************/
int EvaluateExpression(char *postorder, double *result)
{
Stack s;
char *op; //存放后缀表达式中的每个因数或运算符
char *buf = postorder; //声明buf和saveptr两个变量,是strtok_r函数的需要。
char *saveptr = NULL;
double d;
double e, f;
if (CreateStack(&s) != 1)
return 0;
while ((op = strtok(buf, " ")) != NULL)//利用字符串分割函数
{
buf = NULL; //把指针置空
switch (op[0])
{
case '+':
pop(&s, &d);
pop(&s, &e);
f = d + e;
push(&s, f);
break;
case '-':
if (op[1] >= '0' && op[1] <= '9')
{
d = atof(op);
push(&s, d);
break;
}
pop(&s, &d);
pop(&s, &e);
f = e - d;
push(&s, f);
break;
case '*':
pop(&s, &d);
pop(&s, &e);
f = e*d;
push(&s, f);
break;
case '/':
pop(&s, &d);
pop(&s, &e);
f = e / d;
push(&s, f);
break;
default: //考虑数字的情况,进行atoi函数进行转化
d = atof(op);
push(&s, d); //进行压栈
break;
}
}
pop(&s, result);
return 0;
}
void menuone()
{
char inorder[MAXSIZE] = { 0 };
char postorder[MAXSIZE] = { 0 };
int n;
double result;
printf(" \n");
printf(" Welcome to the blizzard calculator\n");
printf(" \n");
do
{
printf("***** 1.Input expression ****\n");
printf("***** 2.Show Calculated results ****\n");
printf("***** 3.Quit ****\n");
printf(" \n");
printf("Please select function >:");
printf(" \n");
scanf("%d",&n);
switch(n)
{
case 3:
printf("***** 感谢您的使用 ****\n");
printf(" \n");
quit();
case 1:
CreateExpression(inorder);
break;
case 2:
TransmitExpression(inorder, postorder);
printf("转化后的后缀表达式是:\n");
printf("%s\n", postorder);
EvaluateExpression(postorder, &result);
printf("计算结果:\n");
printf("%lf\n", result); system("pause");
break;
default:
printf(" 输入错误,请重新输入 \n");
}
} while(n>0);
}
int main()
{
float result = 0;
menuone();
return 0;
}