Evaluate infix expressions

"Calculating infix expressions" can be regarded as a particularly classic algorithm question about stacks, which is covered in almost all data structure textbooks, and many companies will use this question as an inspection point during interviews or written tests. It can be said that this is an algorithm problem that must be mastered. Infix expressions, postfix expressions and other concepts will not be repeated here, let us go straight to the topic.

Question: Enter an infix expression and calculate its result.

Input assumptions:

(1) Only four operators of +, -, *, / are considered, and there is only one parenthesis in the infix expression: ();

(2) There are only integers and no decimals in the input infix expression;

(3) Assume that the input is valid.

Many articles or textbooks like to go all the way and directly discuss how to compute results from infix expressions. But for beginners, the span is a bit too big. Here step by step, from easy to difficult, first discuss how to convert infix expressions into postfix expressions, and then discuss how to calculate postfix expressions. Finally, on the basis of the previous two steps, discuss how to directly calculate the result of the infix expression in one step:

1. How to convert an infix expression into a postfix expression

In everyday applications, operators in arithmetic expressions always appear between two operands, such as 5*(7-2*3)+8/2. This form is called an infix expression. Evaluating an infix expression requires knowledge of operator precedence and associativity. Multiplication and division are high priority, addition and subtraction are low priority, and they are left-associative when the priority is the same, that is, counting from left to right. If there are parentheses, the expressions inside the parentheses are evaluated.

Infix expressions are good for human understanding, but not easy for computers to process. Therefore, it is necessary to convert the infix expression into a postfix expression to facilitate computer processing. The so-called postfix expression is to put the operator after the operand. Postfix expressions are also known as Reverse Polish expressions.

for example:

The infix expression is: 1+(2-3)*4+4/2

The corresponding postfix expression is: 1 2 3 - 4* + 4 2 / +

How to convert an infix expression to a postfix expression? We need to use the power of the stack to store operators. The algorithm flow is as follows:

First order the precedence of the various operators (including parentheses) as follows (the higher the number, the higher the precedence):

1:(

2:+ -

3:* /

4:)

Traverse the input infix expression from left to right:

1) If a number is encountered, add it directly to the end of the suffix expression;

2) If the operators +, -, *, / are encountered:

First check if the stack is empty. If so, push this operator directly onto the stack. If not, look at the current top element of the stack. If the priority of the top element of the stack is greater than or equal to this operator level, the top element of the stack is popped, the top element of the stack is added to the suffix expression, and the above judgment is continued. If the above judgment is not satisfied or the stack is empty, push this operator onto the stack. It should be noted that after the above steps, this operator will eventually be pushed onto the stack.

3) If parentheses are encountered:

If it is a left parenthesis, it is directly pushed onto the stack. If it is a closing parenthesis, pop all operators before the first opening parenthesis on the stack, and pop the opening parenthesis. (Don't push the right parenthesis onto the stack).

4) After the string traversal is over, if the stack is not empty, pop all elements in the stack and add them to the end of the postfix expression until the stack is empty.

2. Calculate the postfix expression

The computation of postfix expressions is quite simple. Prepare a number stack. Scan the suffix expression from left to right, and if it is a number, put it on the number stack. If it is a symbol, pop two numbers from the number stack, the first number taken out is the right operand, the second is the left operand, and the operation is performed. The result is then placed on the number stack. Repeat this until the entire expression is read, and the number left on the number stack is the final result.

The C++ code is as follows. It should be noted that all numbers in the default infix expression of the following code are integers and are between 0 and 9. And the calculation results are all integers (such as 5/2=2).

#include<iostream>
#include<string>
#include<stack>

using namespace std;

int getPriority(char ch)
{
    //get priority
    if (ch == '(') return 1;
    else if (ch == '+' || ch == '-') return 2;
    else if (ch == '*' || ch == '/') return 3;
    else return 4;
}

string getPostfixExpression(string str)
{
    // Convert infix expression to postfix expression
    // default input is valid
    stack<char> mystack;
    int size = str.size();
    int i = 0;
    char tmp;
    string res = "";
    while (i < size) {
        if (str[i] >= '0' && str[i] <= '9'){
            res.push_back(str[i]);
        }
        elseif (str[i] == '+' || str[i] == '-' || str[i] == '*' || str[i] == '/') {
            if (mystack.empty()) {
                mystack.push(str[i]);
            }
            else {
                while (!mystack.empty()) {
                    tmp = mystack.top();
                    if (getPriority(tmp) >= getPriority(str[i])) {
                        //弹出栈顶元素
                        res.push_back(tmp);
                        mystack.pop();
                    }
                    else break;
                }
                mystack.push(str[i]);
            }
        }
        else {
            if(str[i]=='(') mystack.push(str[i]);
            else {
                while (mystack.top() != '(') {
                    tmp = mystack.top();
                    res.push_back(tmp);
                    mystack.pop();
                }
                mystack.pop();
            }
        }
        i++;
    }

    //遍历完后,若栈非空,弹出所有元素
    while (!mystack.empty()) {
        tmp = mystack.top();
        res.push_back(tmp);
        mystack.pop();
    }
    return res;
}

 

int calculator(string str)
{
    //计算后缀表达式的值,默认中缀表达式所有数字都是一位的,在0-9之间
    stack<int> mystack;
    int size = str.size();
    int num1, num2, num3;
    for (int i = 0; i < size; i++) {
        if (str[i] >= '0' && str[i] <= '9') {
            mystack.push(str[i] - '0');
        }
        else {
            num2 = mystack.top();
            mystack.pop();
            num1 = mystack.top();
            mystack.pop();
            if (str[i] == '+') {
                num3 = num1 + num2;
            }
            else if (str[i] == '-') {
                num3 = num1 - num2;
            }
            else if (str[i] == '*') {
                num3 = num1 * num2;
            }
            else if (str[i] == '/') {
                num3 = num1 / num2;
            }
            mystack.push(num3);
        }
    }
    return mystack.top();
}

 
int main()
{
    string str="1+(2-3)*4+4/2";
    cout <<"中缀表达式为:"<< endl << str << endl;
    string res = getPostfixExpression(str);
    cout <<"后缀表达式为:"<< endl << res << endl;
    int num_res = calculator(res);
    cout <<"计算结果:"<< endl << num_res << endl;
    system("pause");
    return 0;
}

三、直接计算中缀表达式

其实将前面的两步结合起来,就可以得到直接计算的方法。准备一个数字栈和一个符号栈。

从左到右遍历中缀表达式。如果遇到数字,入数字栈。

如果遇到符号(四个运算符以及括号),跟前面的“中缀表达式转后缀表达式”过程一样,对符号栈进行处理。处理过程中,对每一个出栈的运算符:+ - * /,根据“计算后缀表达式”的方法,计算结果(跟数字栈配合)。

如果遍历完中缀表达式后符号栈还非空,就继续出符号栈的运算符,计算,直到符号栈为空。最后数字栈剩下的数字就是结果。

下面给出用C++实现“计算中缀表达式”的代码,里面考虑了“数字不止1位”,并且用浮点型来表示最终运算结果。要求中缀表达式中只能包含整数和运算符(不能包含小数),并且是合法的。

#include<iostream>
#include<string>
#include<stack>

using namespace std;

int getPriority(char ch)
{
	//获取优先级
	if (ch == '(') return 1;
	else if (ch == '+' || ch == '-') return 2;
	else if (ch == '*' || ch == '/') return 3;
	else return 4;
}

void calculate(stack<double> &mystack, char operation)
{
	double num1, num2, num3;
	num2 = mystack.top();
	mystack.pop();
	num1 = mystack.top();
	mystack.pop();
	if (operation == '+') {
		num3 = num1 + num2;
	}
	else if (operation == '-') {
		num3 = num1 - num2;
	}
	else if (operation == '*') {
		num3 = num1 * num2;
	}
	else if (operation == '/') {
		num3 = num1 / num2;
	}

	mystack.push(num3);
}

double calculator(string str)
{
	//计算中缀表达式,默认输入是合法的
	stack<double> mystack_number;
	stack<char> mystack_operation;
	int i = 0, j;
	int size = str.size();
	char tmp_operation;
	string tmp_num;
	while (i < size) {
		if (str[i] >= '0' && str[i] <= '9') {
			j = i;
			while (j < size && str[j] >= '0' && str[j] <= '9') { j++; }
			tmp_num = str.substr(i, j - i);
			mystack_number.push(atoi(tmp_num.c_str()));
			i = j;
		}
		else if (str[i] == '+' || str[i] == '-' || str[i] == '*' || str[i] == '/') {
			if (mystack_operation.empty()) {
				mystack_operation.push(str[i]);
			}
			else {
				while (!mystack_operation.empty()) {
					tmp_operation = mystack_operation.top();
					if (getPriority(tmp_operation) >= getPriority(str[i])) {
						//计算
						calculate(mystack_number, tmp_operation);
						mystack_operation.pop();
					}
					else break;
				}
				mystack_operation.push(str[i]);
			}
			i++;
		}
		else {
			if (str[i] == '(') mystack_operation.push(str[i]);
			else {
				while (mystack_operation.top() != '(') {
					tmp_operation = mystack_operation.top();
					//计算
					calculate(mystack_number, tmp_operation);
					mystack_operation.pop();
				}
				mystack_operation.pop();
			}
			i++;
		}

	}
	//遍历完后,若栈非空,弹出所有元素
	while (!mystack_operation.empty()) {
		tmp_operation = mystack_operation.top();
		//计算
		calculate(mystack_number, tmp_operation);
		mystack_operation.pop();
	}
	return mystack_number.top();
}

int main()
{
	string str = "1+(2-3)*4+10/2+2*2+2+2/5";
	cout << "中缀表达式为:" << endl << str << endl;
	double num_res = calculator(str);
	cout << "计算结果:" << endl << num_res << endl;
	system("pause");
	return 0;
}

相信通过这篇文章,大家对这个问题会有所了解。

给出一道思考题:如果加入乘方'^',应该如何处理?要注意,乘方运算是右结合的。

其实很简单。只有两处修改:

1)将乘方添加到优先级中:

1:(

2:+ -

3:* /

4:^

5:)

2)在读中缀表达式的时候,如果读到乘方^,就将它放进符号栈中。因为乘方的优先级是最高的,而且是右结合的,所以无论它前面出现的是什么运算,这些运算都不能执行。而且它本身能否执行也是不知道的,因此只能进栈。

大家自己试试吧~要记住,学习编程,动手去写代码永远是第一位的。

【参考资料】

[1]https://blog.csdn.net/sinat_36118270/article/details/70257547

[2]翁惠玉, 俞勇. 数据结构:思想与实现[M]. 高等教育出版社, 2009.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325544513&siteId=291194637