逆波兰表达式又叫做后缀表达式。逆波兰表示法是波兰逻辑学家J・卢卡西维兹(J・ Lukasewicz)于1929年首先提出的一种表达式的表示方法 [1] 。后来,人们就把用这种表示法写出的表达式称作“逆波兰表达式”。逆波兰表达式把运算量写在前面,把算符写在后面。
逆波兰表达式就是后缀表达式,将运算量写在前面,运算符写在后面,如:
2, 1, +, 3, * 等价于 ((2 + 1) * 3)
下面我们就来实现逆波兰表达式的求值。
对于上面的逆波兰表达式如何理解呢?因为+ - * /都是二元操作符,我们首先需要读取两个操作对象,在读取操作的符号,计算完后再将计算结果填入栈中,进行下一次的计算。
所以我们需要用到一个数字栈,来保存所有的运算对象,然后每当我们遇到一个运算符,就弹出两个数字,再将计算结果填入,等到栈中只剩一个元素的时候,那个元素就是计算结果。
在这里逆波兰表达式使用二维字符数组的形式来表示的,所以我们第一步就需要将所有的数字识别出来
bool isnum(const char* str)
{
if(*str == '-' && strlen(str) == 1)
return false;
if(*str == '*' || *str == '/' || *str == '+')
return false;
return true;
}
在这里有一个问题,就是负数容易和减号冲突,我们读取的时候有可能将数字前面的减号当作数字读取,所以我们可以通过判断字符串中是不是仅有减号没有别的数字,来判断这个到底是减号还是负数。
int getnum(const char* str)
{
int i = 0;
int ret = 0, flag = 1;
if(str[i] == '-')
{
i++;
flag = 0;
}
for(; i < strlen(str); i++)
{
ret = ret * 10 + str[i] - '0';
}
return flag ? ret : - ret;
}
判断完是否是数字后就需要将其从字符串转换为数字,如果第一位是负号,我们就用flag = 0表示,到返回的时候返回负数即可。字符串转换为数字只需要减去一个字符‘0’,但是有一个问题,就是如果数据不仅仅只有一位,所以我们还需要一个循环来把字符串转换为多位数。
int evalRPN(char ** tokens, int tokensSize){
int count, num1, num2, j = 0;
int *stack = (int*)malloc(sizeof(int) * tokensSize);
for(int i = 0; i < tokensSize; i++)
{
if (isnum(tokens[i]))
{
stack[j++] = getnum(tokens[i]);
}
else
{
switch(*tokens[i])
{
case '+':
num1 = stack[--j];
num2 = stack[--j];
stack[j++] = num2 + num1;
break;
case '-':
num1 = stack[--j];
num2 = stack[--j];
stack[j++] = num2 - num1;
break;
case '*':
num1 = stack[--j];
num2 = stack[--j];
stack[j++] = num2 * num1;
break;
case '/':
num1 = stack[--j];
num2 = stack[--j];
if(!num1)
break;
stack[j++] = num2 / num1;
break;
}
}
}
count = stack[j - 1];
return count;
}
当我们遍历字符串时,每当我们碰到数字就将数据推入数字栈,每当我们碰到运算符时就弹出两个数字,然后计算完后再存入数字栈中,当字符串遍历完后,数字栈仅剩的那一个数据就是计算结果。
这里可能出现错误的地方就是两个数据运算时的位置。先弹出的是右运算数,后弹出的是左运算数。
下面来测试一个数据:
10, 6, 9, 3, +, -11, *, /, *, 17, +, 5, +
等价于
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
结果正确
完整代码
bool isnum(const char* str)
{
if(*str == '-' && strlen(str) == 1)
return false;
if(*str == '*' || *str == '/' || *str == '+')
return false;
return true;
}
int getnum(const char* str)
{
int i = 0;
int ret = 0, flag = 1;
if(str[i] == '-')
{
i++;
flag = 0;
}
for(; i < strlen(str); i++)
{
ret = ret * 10 + str[i] - '0';
}
return flag ? ret : - ret;
}
int evalRPN(char ** tokens, int tokensSize){
int count, num1, num2, j = 0;
int *stack = (int*)malloc(sizeof(int) * tokensSize);
for(int i = 0; i < tokensSize; i++)
{
if (isnum(tokens[i]))
{
stack[j++] = getnum(tokens[i]);
}
else
{
switch(*tokens[i])
{
case '+':
num1 = stack[--j];
num2 = stack[--j];
stack[j++] = num2 + num1;
break;
case '-':
num1 = stack[--j];
num2 = stack[--j];
stack[j++] = num2 - num1;
break;
case '*':
num1 = stack[--j];
num2 = stack[--j];
stack[j++] = num2 * num1;
break;
case '/':
num1 = stack[--j];
num2 = stack[--j];
if(!num1)
break;
stack[j++] = num2 / num1;
break;
}
}
}
count = stack[j - 1];
return count;
}