前段时间学习了栈的基础操作后,想着用栈实现四则运算来巩固以下所学知识。
什么是栈
栈(Stack)是规定只能在表尾进行插入和删除操作的线性表。这一规定决定了栈中数据具有后入先出的特点。
由图可知,可以通过栈顶元素、存储元素、栈的最大空间来一起描述一个栈。对于顺序栈,存在一个定义栈的最大空间的参数使顺序栈具有固定的大小。如果想要一个没有存储元素个数限制的栈,则可以模仿动态链表的思路,让栈中元素包含有一个指向下一个元素地址的指针,从而形成一个链栈,这也正是实现四则运算所需要的。
实现思路
首先是实现要用到的栈的操作,分别是元素入栈、元素出栈、获取栈顶元素
栈的基本定义:
typedef enum Status
{
ERROR = 0,
SUCCESS = 1
} Status;
typedef struct StackNode
{
int data;
struct StackNode *next;
}Node;
typedef struct StackInfo
{
struct StackNode *top;
int count;
}Stack;
入栈
1、形成新结点
2、新节结点的直接后继指向当前的栈顶元素
3、栈顶指针指向新结点
Status Push(Stack *s, int e)
{
if (s == NULL)
{
return ERROR;
}
Node *p = (Node *)malloc(sizeof(Node));
if (p == NULL)
{
return ERROR;
}
p->data = e;
p->next = s->top;
s->top = p;
s->count = s->count + 1;
return SUCCESS;
}
出栈
1、判断栈顶是否为空
2、p指向栈顶元素,栈顶指针下移一位
3、释放p所指向的空间
Status Pop(Stack *s)
{
if (s == NULL)
{
return ERROR;
}
if (s->top == NULL)
{
return ERROR;
}
int save;
Node *p = s->top;
save = s->top->data;
s->top = p->next;
free(p);
p = NULL;
s->count = s->count - 1;
return save;
}
得到栈顶元素
Status GetTop(Stack *s)
{
if (s == NULL)
{
return ERROR;
}
if (s->top == NULL)
{
return ERROR;
}
return s->top->data;
}
栈的判空
Status EmptyStack(Stack *s)
{
if (s == NULL)
{
return ERROR;
}
if(s->top == NULL)
{
return SUCCESS;
}
else{
return ERROR;
}
}
接下来,先理解以下四则运算的实现原理
观察式子:1+2 = 3
平时我们所用的四则运算表达式都是运算符在两个数字中间的,所以称这种表达式为中缀表达式。
而后缀表达式定义为运算符在两个数字之后的表达式。例如:
式子「1」:6 + ( 4 - 2 ) × 3 + 9 ÷ 3 = 15
式子「2」:6 4 2 - 3 × + 9 3 ÷ + = ?
式子「1」是中缀表达式,式子「2」是后缀表达式
与中缀表达式相比,后缀表达式的优点是不需要考虑括号匹配、运算符优先级和简化运算。
我们利用链栈来表示后缀表达式,从而可以计算出结果。所以首先要将中缀表达式转换为后缀表达式,方法如下:
从头到尾读取中缀表达式的每个对象,对不同对象按不同情况处理。
1 运算数:直接输出
2 左括号:压入堆栈
3 右括号:将栈顶元素弹出直到遇到左括号(出栈,不输出)
4 运算符:
若优先级大于栈顶元素,将它压栈
若优先级小于栈顶元素,栈顶元素弹出并输出;再比较新的栈顶元素,直到该运算符大于栈顶运算符优先级为止,然后将该运算符号压栈
5 各对象处理完毕后,把堆栈中存留的运算符一并输出
优先级如代码所示:
int Priority(char ch) /*判断优先级*/
{
switch(ch)
{
case '(':
return 3;
case '*':
case '/':
return 2;
case '+':
case '-':
return 1;
default:
return 0;
}
}
接下来是后缀表达式的运算
规则:(用栈来进出运算的数字)
1.从左到右遍历后缀表达式的每一个数字和符号
2.若是数字,则进栈
3.若是符号,则把处于栈顶的两个数字出栈,进行运算
4.运算结果进栈
5.直到获得最终结果
最后附上主函数的完整代码
int main()
{
int i = 0;
int num1,num2;
int tem = 0; /*用来暂时存储数据*/
char str[111] = {0}; /*存储输入的运算式*/
Stack *res,*ope; /*存储运算式中输出的和不输出的*/
if(!(InitStack(&res)) || !(InitStack(&ope))){
printf("初始化失败\n");
}
printf("请输入您的式子:");
scanf("%s",str);
while(str[i] != '\0' || ope->count != 0){ /*遍历运算式*/
if('0'<=str[i] && str[i]<='9'){
tem = tem*10+str[i]-'0'; /*将字符型转换为整型*/
i++;
if('0'>=str[i] || str[i]>='9'){
Push(res,tem); /*如果数字的下一位不是整型,则将数字存储,否则这是一个多位数*/
tem = 0;
}
}
else{ /*当遇到运算符时*/
/*首先是运算符入栈的情况*/
if (GetTop(ope) == '(' && str[i] ==')') //直接出栈,不计算,栈顶为'(' ,表达式为')'
{
Pop(ope); //括号直接出栈
i++;
continue; //继续下一次循环
}
if((EmptyStack(ope) == SUCCESS) || (Priority(str[i]) > Priority(GetTop(ope))) || (GetTop(ope) == '(' && str[i] != ')')) /*存储运算符的栈为空、优先级*/
{
Push(ope,str[i]);
i++;
continue;
}
/*接下来是运算符输出并且运算的情况*/
if((Priority(str[i]) <= Priority(GetTop(ope)))|| str[i] == ')'|| (str[i] == '\0' && EmptyStack(ope) != SUCCESS))
{/*优先级、遇到右括号、运算式结束而存储运算的栈还未结束*/
int num;
num1 = Pop(res); //数字栈顶出栈
num2 = Pop(res); //数字栈第二个数字出栈
switch (Pop(ope)) //后出的数字在前
{
case '+':
num = num1 + num2;
Push(res,num); /*运算后在放入栈内*/
break;
case '-':
num = num2 - num1;
Push(res,num);
break;
case '*':
num = num1 * num2;
Push(res,num);
break;
case '/':
num = num2 / num1;
Push(res,num);
break;
}
}
}
}
printf("result = %d",GetTop(res));
return 0;
}