1、问题的提出
尝试用计算机读入字符串“9+(3-1)*5+8/2+(5/2)”并计算值
2、中缀表达式 与 后缀表达式
波兰科学家在20世纪50年代提出了一种将运算符放在数字后面的后缀表达式
对应的,我们习惯的数学表达式叫做中缀表达式
实例:
5 + 3 => 5 3 +
1 + 2 * 3 => 1 2 3 * +
9 + ( 3 – 1 ) * 5 => 9 3 1 – 5 * +
中缀表达式符合人类的阅读和思维习惯
后缀表达式符合计算机的“运算习惯”
3、将中缀表达式转换成后缀表达式?
实现思路:
- 遍历中缀表达式中的数字和符号
- 对于数字:直接输出 直接输出
- 对于符号:
• 左括号:进栈
• 符号:与栈顶符号进行优先级比较
• 栈顶符号优先级低:进栈
• 栈顶符号优先级不低:将栈顶符号弹出并输出,之后进栈
• 右括号:将栈顶符号弹出并输出,直到匹配左括号 - 遍历结束:将栈中的所有符号弹出并输出
4、实现代码
/*
中缀表达式到后缀表达式的转换
参数:
str: 指向中缀表达式的指针,传入参数
retval: 指向返回结果的后缀表达式,传出参数
*/
void transform(const char *str,char *retval)
{
LinkStack *stack = LinkStack_Create();
int i = 0,j = 0;
//判断返回数据的指针是否有效
if(retval == NULL)
{
return;
}
while(str[i] != '\0')
{
//如果是数字,直接输出它
if(isNumber(str[i]))
{
retval[j] = str[i];
j++;
}
else
if(isOperator(str[i]))
{
/*
如果是运算符,则检查栈顶符号优先级是否大于等于当前符号优先级;
如果是,则弹出栈顶运算符并输出,然后进栈当前优先级低的运算符
如果否,即栈顶的运算符优先级低,则直接进栈
特殊情况,栈中没有元素时,不用比较优先级,直接进栈
*/
char *top = (char*)LinkStack_Top(stack);
if(top != NULL)
{
if(priority(*top) >= priority(str[i]))
{
retval[j] = *(char*)LinkStack_Pop(stack);
j++;
}
}
LinkStack_Push(stack,(LinkStackNode*)(str + i));
}
else
if(isLeft(str[i]))
{
//如果是左括号,直接进栈
LinkStack_Push(stack,(LinkStackNode*)(str + i));
}
else
if(isRight(str[i]))
{
/*
如果是右括号,依次弹出栈顶元素,并输出,知道匹配左括号
即是,知道栈顶弹出的是左括号为止
但是,并不输出左右括号
*/
char temp = '\0';
while(!isLeft(temp))
{
if(temp != '\0')
{
retval[j] = temp;
j++;
}
temp = *(char*)LinkStack_Pop(stack);
}
}
else
{
printf("Invalid expression!");
break;
}
i++;
}
/*
如果遍历结束,并且栈中还有数据,那么依次弹出它们,并输出
这里判断str[i] == '\0' 是因为可能中缀表达式中有为止符号,
那么将使上面的循环提前终止,并没有检测完中缀表达式,即使这时候
栈中有元素,那转换还是错误的!!
*/
while((LinkStack_Size(stack) > 0) && (str[i] == '\0'))
{
retval[j] = *(char*)LinkStack_Pop(stack);
j++;
}
LinkStack_Destroy(stack);
}
5、对后缀表达式的计算
计算机对后缀表达式的运算也是基于栈的!
实现思路:
- 遍历后缀表达式中的数字和符号
- 对于数字:进栈
- 对于符号:
• 从栈中弹出右操作数
• 从栈中弹出左操作数
• 根据符号进行运算
• 将运算结果压入栈中 - 遍历结束:栈中的唯一数字为计算结果
6、实现代码
/*
用于计算后缀表达式的值
参数str:指向后缀表达式的指针
*/
DataType compute(const char *str)
{
LinkStack *stack = LinkStack_Create();
DataType iret = 0,i = 0;
while(str[i] != '\0')
{
//如果是数据,进栈
if(isNumber(str[i]))
{
//直接存放DataType类型数据的值
LinkStack_Push(stack,(LinkStackNode*)chartoDataType(str[i]));
}
else
if(isOperator(str[i]))
{
//如果是操作符,则依次弹出栈顶,分别为该操作符的右操作数和左操作数
DataType right = (DataType)LinkStack_Pop(stack);
DataType left = (DataType)LinkStack_Pop(stack);
//计算它们的值,然后将其压栈
DataType result = express(left,right,str[i]);
LinkStack_Push(stack,(LinkStackNode*)result);
}
else
{
printf("Invalid expression!\n");
break;
}
i++;
}
/*
运算结束,如果栈中只有一个数据,并且str[i] == '\0'
那么运算是成功的;
判断str[i] == '\0'是因为如果后缀表达式中有非运算符和数值的字符,
循环会提前结束,导致结果是不正确的
*/
if(LinkStack_Size(stack) == 1 && str[i] == '\0')
{
//获取最终的结果
iret = (DataType)LinkStack_Pop(stack);
}
LinkStack_Destroy(stack);
return iret;
}
7、完整源码下载
文件名:compute-1.0.tar.gz
链接: http://pan.baidu.com/s/1sjHTaMx 密码: twd5
文件名:compute-1.1.tar.gz
链接: http://pan.baidu.com/s/1hq1qFA0 密码: nufi
说明:修复了由linklist.c中的List_Get()函数所引起的BUG,详情见链接!
编译步骤:
0.1 解压缩:tar -zxvf compute-1.0.tar.gz
0.2 进入目录:./configure
0.3 生成Seqlist:make
0.4 运行程序:./Compute