C++ 中缀表达式转后缀

这几天学习编译原理,发现词法分析中需要用到这个中缀表达式转后缀,但当初学数据结构的时候就理解的不是很清楚,因此又学习了一下。

转化算法还是比较容易和清晰的,当然,前提是使用好的教材。

先给出转化算法的整体流程,然后进行代码实现。

转化步骤

建立一个存放操作符的空栈,以及一个存放后缀表达式的空字符串post,然后遍历中缀表达式,根据当前不同的字符进行不同的操作:

  1. 若当前为数字,则直接放在post末尾
  2. 若当前为字符,记为c,然后将当前栈顶字符记为t,根据ct的优先级进行不同操作:
    1. 若当前栈为空,则将c压入栈
    2. 若优先级t > c,则弹出t,放到post末尾,然后c继续与新栈顶比较
    3. 若优先级t < c,则将c压入栈
    4. 若优先级t = c,注意此时t必为'('c必为')',弹出t

在遍历结束后,将栈中剩余的字符都放入post,至此,转化完成。

字符优先级表

上面提到了操作符的优先级,为了简化实现代码,我们先根据优先级构造优先级表,如下所示:

const char precedence_table_[9][9] = {
//    +    -    *    /    %    ^    !    (    )
    {'>', '>', '<', '<', '<', '<', '<', '<', '>'}, // +
    {'>', '>', '<', '<', '<', '<', '<', '<', '>'}, // -
    {'>', '>', '>', '>', '>', '<', '<', '<', '>'}, // *
    {'>', '>', '>', '>', '>', '<', '<', '<', '>'}, // /
    {'>', '>', '>', '>', '>', '<', '<', '<', '>'}, // %
    {'>', '>', '>', '>', '>', '>', '<', '<', '>'}, // ^
    {'>', '>', '>', '>', '>', '>', '>', ' ', '>'}, // !
    {'<', '<', '<', '<', '<', '<', '<', '<', '='}, // (
    {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}  // )
}

其中行表示栈顶字符t,列表示输入字符c,举个例子,precedence_table_[1][3]表示,当栈顶t-,此时输入c/时,结果为<,即此时优先级t < c

代码实现

API

先看一下整体结构与功能:

class Converter {
private:
	const char operator_list_[9];
	const int ol_size = 9;
	const char precedence_table_[9][9];
    
	string expression_;
	int e_size_;

	char compare(char a, char b) const;
	int find(char c) const;
	string read_digit(int& lo) const;
public:
	void input(string expression);
	string convert() const;
};

其中:

  1. operator_list_如下所示:
{'+', '-', '*', '/', '%', '^', '!', '(', ')'};

​ 注意,operator_list_中字符的顺序应该与字符优先级表中一致

  1. compare(a,b)用于比较字符的优先级
  2. find(c)用于查找字符在opeartor_list_中的下标
  3. read_digit(i)用于读取数字,因为数字可能有多位以及小数点
  4. convert()实现转化的算法

主要逻辑

convert()中实现上面所说的算法:

string Converter::convert() const {
	stack<char> s;
	int i = 0;
	string postfix = "";
	while (i < e_size_) {
		if (isdigit(expression_[i])) { // digit
            // i is increased in read_digit
			string digit = read_digit(i).append(" "); 
			postfix.append(digit);
		}
		else if (expression_[i] == ' ') i++; // skip ' '
		else { // operator
			if (s.empty()) s.push(expression_[i++]);
			else {
				char top = s.top(), input = expression_[i];
				char precedence = compare(top, input);
				if (precedence == '<') {
					s.push(input); i++;
				}
				else if (precedence == '>') {
					s.pop(); postfix.push_back(top);
				}
				else if (precedence == '=') {
					s.pop(); i++;
				}
			}
		}
	}
    // clean stack
	while (!s.empty()) {
		char top = s.top();
		if (top == '(') {
			cerr << "There is a '(' not matching with a ')'";
			exit(0);
		}
		postfix.push_back(s.top());
		s.pop();
	}
	return postfix;
}

辅助函数

input

用于读入中缀表达式:

void Converter::input(string expression) {
	expression_ = expression;
	e_size_ = expression_.length();
}
find

查找coperator_list_中的下标,用于计算优先级。

int Converter::find(char c) const {
	for (int i = 0; i < ol_size; i++) {
		if (c == operator_list_[i]) return i;
	}
	return -1;
}
compare

比较两个字符的优先级:

char Converter::compare(char top, char input) const {
	int i1 = find(top), i2 = find(input);
	if (i2 == -1) { // i1 must be >= 0
		string error = "The input contains illegal char: ";
		error.push_back(input);
		cerr << error << endl;
		exit(0);
	}
	return precedence_table_[i1][i2];
}
read_digit

读取一个完整的数字,可能为浮点数以及多位数:

string Converter::read_digit(int& lo) const {
	string digit = "";
	bool dot = false;
	for (; lo < e_size_; lo++) {
		char c = expression_[lo];
		if (isdigit(c))
			digit.push_back(c);
		else if (c == '.') {
			if (!dot) {
				digit.push_back(c);
				dot = true;
			}
			else {
				cerr << "The expression contains illegal char '.' in " + lo
					<< endl;
				exit(0);
			}
		}
		else break;
	}
	return digit;
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值