OI学习笔记10:后缀表达式

后缀表达式

一、定义

1、中缀表达式。

  • 中缀表达式是人类善于、也是最常用的一种表达式形式,通常被描述为 \(A\) \(op\) \(B\),其中 \(op\) 为运算符。
  • 例如:\(1-(3+2)*2\) 就是一种较复杂的中缀表达式。

2、后缀表达式。

  • 后缀表达式是计算机最能理解的表达式形式。由于计算机的堆栈结构,使得其能够快速地求解后缀表达式。
  • 后缀表达式被描述为 \(A\) \(B\) \(op\),其中 \(op\) 为运算符。
  • 例如:\(1\ 2 + 3\ *\) 就是一种较为简单的后缀表达式。
  • 更学术地,我们有关于后缀表达式的如下定义:
  1. 如果 \(E\) 是一个操作数,则 \(E\) 的后缀表达式是其本身。
  2. 如果 \(E\)\(E_1\ op\ E_2\) 形式的表达式,其中 \(op\) 是任何二元操作符,且优先级不高于 \(E_1\)\(E_2\) 中括号外的操作符,则 \(E\) 的后缀表达式为 \(E'_1\ E'_2\ op\),其中 \(E'_1\)\(E'_2\) 分别是 \(E_1\)\(E_2\) 的后缀表达式。
  3. 如果 \(E\)\(E_1\) 形式的表达式,则 \(E_1\) 的后缀表达式就是 \(E\) 的后缀表达式。

二、后缀表达式相关算法

1、后缀表达式求值。

(1)算法流程。

  1. 给定后缀表达式,求其值。我们可以用栈来 \(O(N)\) 地求出值。
  2. 建立一个用于存数的栈,逐一扫描该后缀表达式中的元素。
    1. 如果遇到一个数,则把该数入栈。
    2. 如果遇到运算符,就取出栈顶的两个数进行计算,把结果入栈。
  3. 扫描完成后,栈中恰好剩下一个数,就是该后缀表达式的值。

(2)代码实现。

int getValue(string s){//s为后缀表达式
	stack<int> st;//存数字的栈
	for(int i = 0; i < s.length(); i++){
		if(isdigit(s[i])) st.push(s[i] - '0');
		else{
			int n2 = st.top(); st.pop();//弹数字
			int n1 = st.top(); st.pop();
			//计算
			if(s[i] == '+') st.push(n1 + n2);
			if(s[i] == '-') st.push(n1 - n2);
			if(s[i] == '*') st.push(n1 * n2);
			if(s[i] == '/') st.push(n1 / n2);
			if(s[i] == '^') st.push(pow(n1, n2));
		}
	}
	return st.top();
}

2、中缀表达式转后缀表达式。

(1)算法流程。

  1. 建立一个用于存运算符的栈,逐一扫描该中缀表达式中的元素。
    1.如果遇到一个数,输出该数。
    2.如果遇到左括号,把左括号入栈。
    3.如果遇到右括号,不断取出栈顶并输出,直到栈顶为左括号,然后将左括号出栈。
    4.如果遇到运算符,只要栈顶符号的优先级不低于新符号,就不断取出栈顶并输出,最后把新符号入栈。优先级为乘除\(>\)加减\(>\)左括号。
  2. 依次取出并输出栈中所有的剩余符号,最终输出的序列就是一个与原中缀表达式等价的后缀表达式。

(2)代码实现。

int lev(char c){//返回符号的优先级,此函数可以使用 map<char. int> 代替
	if(c == '+' || c == '-') return 1;
	if(c == '*' || c == '/') return 2;
	if(c == '^') return 3;
	if(c == ')' || c == '(') return 0;
	return -1;
}
string toSuffix(const string &s){//转换
	string ret = "";
	stack<char> st;
	for(int i = 0; i < s.length(); i++){
		if(isdigit(s[i])) ret += s[i];
		else if(s[i] == '(') st.push(s[i]);
		else if(s[i] == ')'){
			while(st.top() != '(')
				ret += st.top(), st.pop();
			st.pop();
		}
		else{
			while(!st.empty() && lev(st.top()) >= lev(s[i]))
				ret += st.top(), st.pop();
			st.push(s[i]);
		}
	}
	while(!st.empty())
		ret += st.top(), st.pop();
	return ret;
}

三、例题。

P1175 表达式的转换

平常我们书写的表达式称为中缀表达式,因为它将运算符放在两个操作数中间,许多情况下为了确定运算顺序,括号是不可少的,而中缀表达式就不必用括号了。
后缀标记法:书写表达式时采用运算紧跟在两个操作数之后,从而实现了无括号处理和优先级处理,使计算机的处理规则简化为:从左到右顺序完成计算,并用结果取而代之。
例如:8-(3+2*6)/5+4 可以写为:8 3 2 6*+5/-4+
其计算步骤为:

8 3 2 6 * + 5 / – 4 +
8 3 12 + 5 / – 4 +
8 15 5 / – 4 +
8 3 – 4 +
5 4 +
9

编写一个程序,完成这个转换,要求输出的每一个数据间都留一个空格。

一道中缀表达式转后缀表达式加后缀表达式计算的板子题,具体在代码中体会:

#include<cstdio>
#include<bits/stdc++.h>
#include<list>
using namespace std;
inline int lev(char c){
	if(c == '+' || c == '-') return 1;
	if(c == '*' || c == '/') return 2;
	if(c == '^') return 3;
	if(c == ')' || c == '(') return 0;
	return -1;
}
inline int power(int a, int b){
	int ans = 1;
	while(b){
		if(b & 1) ans *= a;
		a *= a;
		b >>= 1;
	}
	return ans;
}
inline string toSuffix(const string &s){//中缀表达式转后缀表达式
	string ret = "";
	stack<char> st;
	for(int i = 0; i < s.length(); i++){
		if(isdigit(s[i])) ret += s[i];
		else if(s[i] == '(') st.push(s[i]);
		else if(s[i] == ')'){
			while(st.top() != '(')
				ret += st.top(), st.pop();
			st.pop();
		}
		else{
			while(!st.empty() && lev(st.top()) >= lev(s[i]))
				ret += st.top(), st.pop();
			st.push(s[i]);
		}
	}
	while(!st.empty())
		ret += st.top(), st.pop();
	return ret;
}
inline int calcNum(int n1, int n2, char s){
	if(s == '+') return n1 + n2;
	else if(s == '-') return n1 - n2;
	else if(s == '*') return n1 * n2;
	else if(s == '/') return n1 / n2;
	else if(s == '^') return power(n1, n2);
}
inline void calc(string s){//计算并输出过程
	int tail = s.length();
	list<int> st;
	for(int i = 0; i < tail; i++){
		printf("%c ", s[i]);
	}
	printf("\n");
	for(int i = 0; i < tail; i++){
		if(isdigit(s[i])){
			st.push_back(s[i] - '0');
		}
		else{//这里计算使用更为方便的list,而不是使用stack,使用stack不便于输出过程。
			int n2 = st.back(); st.pop_back();
			int n1 = st.back(); st.pop_back();
			st.push_back(calcNum(n1, n2, s[i]));
			
			for(list<int>::iterator it = st.begin(); it != st.end(); it++)
				printf("%d ", *it);//输出迭代器。
			for(int j = i + 1; j < tail; j++)
				printf("%c ", s[j]);
			printf("\n");
		}
	}
}
int main(){
	string b;
	cin>>b;
	calc(toSuffix(b));
	return 0;
}
posted @ 2021-09-06 18:32  聂玄HankNie  阅读(596)  评论(0编辑  收藏  举报