您的位置:首页 > 其它

栈的应用--计算字符串表达式

2014-06-11 22:55 316 查看
计算机的本质工作就是做数学运算,那计算机可以读入字符串"1+2+3+4+5+6+7"并计算值吗?
答案是肯定的。
这里我基本实现了个位数的加减乘除,当然这个算法最简单的解决方式是采用二叉树(后面会实现~),这里作出了栈的实现方式。

首先引入两个概念:
中缀表达式和后缀表达式
1,在生活中我们通常书写1+1的时候都会写成1+1,废话~,这就是中缀表达式,更符合人们的思维习惯与想法。
2,所谓后缀表达式就是将运算符写在操作数的后面,这样更符合计算机的思维。
举例:
 
5 + 3            =>  5 3 +
1 + 2 * 3        =>
  1 23 * +
9 + ( 3 – 1 ) * 5 =>  9 3 1 – 5 * +

为什么会这样的呢?我来用我的大白话作一下解释,比如 9
+ ( 3 – 1 ) * 5 =>   9 3 1 – 5 * +
后缀表达式在实现计算的时候,遇到符号就向左寻找操作数,比如上面的9 3 1后面就有 - 号,此时计算机就会将3作为左操作数,1作为右操作数,也就是计算3-1的值,继续前进,遇到5
pass,遇到*号,就会将刚刚计算好的3-1的值作为左操作数,5作为右操作数,即为(3-1)*5,继续前进,遇到+号,就会计算9+(3-1)*5的值。就是这样~


那么计算机是如何将中缀表达式转换为后缀表达式和如何将后缀表达式的值求出来的呢?

中缀转后缀:

遍历中缀表达式中的数字和符号
对于数字:直接输出
对于符号:
  ->左括号:进栈
  ->符号:与栈顶符号进行优先级比较
        栈顶符号的优先级低:符号进栈
        栈顶符号的优先级高:将栈顶符号弹出并输出,之后进栈
  ->右括号:将栈中的所有符号弹出并输出

计算后缀的值:

遍历后缀表达式中的数字和符号
对于数字:进栈
对于符号:
  ->从栈中弹出右操作数
  ->从栈中弹出左操作数
  ->根据符号进行运算
  ->将运算结果压入栈中
 遍历结束:栈中的唯一数字为计算结果

好了,我们来用代码实现以下,跟上一篇一样,这里同样需要用到栈的代码,同样这里就不再贴了,请参阅: 栈的实现与操作(C语言实现)

// 栈的应用计算表达式.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <memory.h>
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include "LinkStack.h"
#include <malloc.h>
//判断是否为数字
int isNumber(char c)
{
return ('0' <= c) && (c <= '9');
}
//判断是否为操作符
int isOperator(char c)
{
return (c == '+') || (c == '-') || (c == '*') || (c == '/');
}
//判断是否为:(
int isLeft(char c)
{
return (c == '(');
}
//判断是否为:)
int isRight(char c)
{
return (c == ')');
}
//比较优先级
int priority(char c)
{
int ret = 0;

if( (c == '+') || (c == '-') )
{
ret = 1;
}

if( (c == '*') || (c == '/') )
{
ret = 2;
}

return ret;
}
//输出
void output(char c)
{
if( c != '\0' )
{
printf("%c", c);
}
}

//中缀转后缀
void transform(const char* exp)
{
LinkStack* stack = LinkStack_Create();
int i = 0;

while( exp[i] != '\0' )
{
//判断是否为数字
if( isNumber(exp[i]) )
{
output(exp[i]);

}
//判断是否为操作符
else if( isOperator(exp[i]) )
{
//遍历栈内符号,如果当前操作符的优先级小于栈顶的操作符,则弹出栈顶,并输出
while( priority(exp[i]) <= priority((char)(int)LinkStack_Top(stack)) )
{
output((char)(int)LinkStack_Pop(stack));

}
//将当前操作符压入栈
LinkStack_Push(stack, (void*)(int)exp[i]);
}
//判断是否为左括号:( ,如果是,则压入栈
else if( isLeft(exp[i]) )
{
LinkStack_Push(stack, (void*)(int)exp[i]);
}
//判断是否为右括号: )
else if( isRight(exp[i]) )
{
char c = '\0';//没看出有什么用,哈哈

//遍历栈内元素,如果不是左括号:(,就一直输出
while( !isLeft((char)(int)LinkStack_Top(stack)) )
{
output((char)(int)LinkStack_Pop(stack));
}
//弹出左括号:(
LinkStack_Pop(stack);
}
else
{
printf("Invalid expression!");
break;
}

i++;
}
//弹出栈内所有元素
while( (LinkStack_Size(stack) > 0) && (exp[i] == '\0') )
{
output((char)(int)LinkStack_Pop(stack));
i++;

}
//销毁栈
LinkStack_Destroy(stack);

}

int value(char c)
{
return (c - '0');
}

//计算左右操作数的值
int express(int left, int right, char op)
{
int ret = 0;

switch(op)
{
case '+':
ret = left + right;
break;
case '-':
ret = left - right;
break;
case '*':
ret = left * right;
break;
case '/':
ret = left / right;
break;
default:
break;
}

return ret;
}

//计算整个表达式的值
int compute(const char* exp)
{
LinkStack* stack = LinkStack_Create();
int ret = 0;
int i = 0;

while( exp[i] != '\0' )
{
if( isNumber(exp[i]) )
{
LinkStack_Push(stack, (void*)value(exp[i]));

}
else if( isOperator(exp[i]) )
{
int right = (int)LinkStack_Pop(stack);
int left = (int)LinkStack_Pop(stack);
int result = express(left, right, exp[i]);

LinkStack_Push(stack, (void*)result);
}
else
{
printf("Invalid expression!");
break;
}

i++;
}

if( (LinkStack_Size(stack) == 1) && (exp[i] == '\0') )
{
ret = (int)LinkStack_Pop(stack);
}
else
{
printf("Invalid expression!");
}

LinkStack_Destroy(stack);

return ret;
}

int _tmain(int argc, _TCHAR* argv[])
{
printf("8 * 2 + 1 - ( 5 - 1 ) / 2 + 2 - 1中缀转后缀后结果为:");
transform("8*2+1-(5-1)/2+2-1");
printf("\n");
printf("8 * 2 + 1 - ( 5 - 1 ) / 2 + 2 - 1 = %d\n", compute("82*1+51-2/-2+1-"));

system("pause");
return 0;
}


运行结果:

8 * 2 + 1 - ( 5 - 1 ) / 2 + 2 - 1中缀转后缀后结果为:82*1+51-2/-2+1-
8 * 2 + 1 - ( 5 - 1 ) / 2 + 2 - 1 = 16
请按任意键继续. . .


如有错误,望不吝指出。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息