简单计算器(栈、中缀表达式转后缀表达 详解)

本文详细介绍了如何使用栈来将给定的含加减乘除的非负整数中缀表达式转换成后缀表达式,并通过实例演示了解决过程。涉及操作符优先级、栈的运用和后缀表达式的计算方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Foremost

。。。

Decription

给定一串含+,-,*,/的非负整数计算表达,计算该表达式的值
以0为结束点。

Sample

input

30 / 90 - 26 + 97 - 5 - 6 - 13 / 88 * 6 + 51 / 29 + 79 * 87 + 57 * 92
0

output

12178.21

Solution

其实这就是简单的栈的应用。属于经典题型,值得记录。
而且自己也好久没打过了,正好写来玩一玩,理清一下思路。

题目给出的是中缀表达式,所以要计算它的值主要是两个步骤:

  1. 中缀表达式转后缀表达式
  2. 计算后缀表达式的值。

首先处理 中缀表达式转后缀表达式

  1. 设立一个操作符栈(加减乘除),用以临时存放操作符
    设立一个数组或者队列,用以存放后缀表达式
  2. 从左至右扫描中缀表达式,如果碰到数字(注意:扫描的过程中要注意数的完整性,可能会有个位十位百位以此类推),就把数字加入到后缀表达式中。
  3. 如果碰到操作符op,就将其优先级与操作符栈的栈顶操作符的优先级比较。
    如果op的优先级高于栈顶操作符的优先级,则压入操作符栈
    如果op的优先级低于或等于栈顶操作符的优先级,则将操作符栈的操作符不断弹出到后缀表达式中,直到op的优先级高于栈顶操作符的优先级。
  4. 重复上述操作,直到中缀表达式扫描完毕,之后若操作符栈中仍有元素,则将它们依次弹出至后缀表达式中。

所谓操作符的优先级就是它们的计算优先级,乘法=除法>加法=减法,在具体实现上可以用map建立操作符和优先级的映射,优先级可以用数字表示。

Q1:为什么当op高于栈顶时就要压入操作符栈?
A1:这里举个例子:对于中缀表达式3+25,显然如果先计算加法3+2会引起错误,必须先计算乘法25.当从左到右扫描时,加号先进入操作符栈,而由于乘号优先级大于加号,其必须先计算,因此在后缀表达式中乘号必须在加号前面,于是在栈中乘号要比加号更靠近栈顶,以让其优先于加号进入后缀表达式。

Q2:为什么当op等于栈顶时不能直接压入操作符栈
A2:这里举个例子:对于中缀表达式 2/3*4,如果设定优先级相等时直接压入操作符栈,那么算法步骤如下:
a) 2 进入后缀表达式,当前后缀表达式为2
b)/进入操作符栈,当前操作符栈为/
c)3进入后缀表达式,当前后缀表达式为23
d)*与操作符栈的栈顶元素/比较,相等,压入操作符栈,当前操作符栈为/ *
e)4进入后缀表达式,当前后缀表达式为234
f)中缀表达式扫描完毕,操作符栈非空,将其全部弹入后缀表达式,最终后缀表达式变为234 * /
g)计算该后缀表达式,发现其实变成了2 / (3 * 4),显然和原来中缀表达式的计算结果完全不同。

括号的情况要特殊处理
如果遇到的操作符是左括号"(”,就直接将该操作符输出到堆栈当中。该操作符只有在遇到右括号“)”的时候移除。

如果扫描到的操作符是右括号“)”,将堆栈中的操作符导出(pop)到后置表达式中,直到遇见左括号“(”。将堆栈中的左括号移出堆栈(pop )。继续扫描下一个字符

然后计算后缀表达式

从左到右扫描后缀表达式,如果是数字,就压入栈;如果是操作符,就连续弹出两个操作符(**注意:**后弹出的是第一操作数,先弹出的是第二操作数),然后进行操作符的操作,生成的新数重新压入栈中。反复直到后缀表达式扫描完毕,这时栈中会只存在一个数,就是最终答案。

CODE

(STL version)

#include <bits/stdc++.h>

using namespace std;

struct node
{
	double num;
	char op;
	bool flag; // true -> number false -> op
};

string str;
stack <node> S; //op stack
queue <node> Q; //Postfix Expression
map<char,int> OP;

void InfixToPostfix()
{
	double num;
	node a;
	for (int i = 0;i < str.length();)
	{
		if (str[i] >= '0' && str[i] <= '9')
		{
			a.flag = true;
			a.num = str[i ++] - '0';
			while (i < str.length() && str[i] >= '0' && str[i] <= '9')
			{
				a.num = a.num * 10 + (str[i] - '0');
				i ++;
			} 	
			Q.push(a);
		}	
		else
		{
			a.flag = false;
			while (! S.empty() && OP[str[i]] <= OP[S.top().op])
			{
				Q.push(S.top());
				S.pop();
			}
			a.op = str[i];
			S.push(a);
			i ++;
		}
	}
	while (! S.empty())
	{
		Q.push(S.top());
		S.pop();
	}
}

double CalcPostfix()
{
	double a,b;
	node head,c;
	while (!Q.empty())
	{
		head = Q.front();
		Q.pop();
		if (head.flag == true) S.push(head);
		else
		{
			b = S.top().num;
			S.pop();
			a = S.top().num;
			S.pop();
			c.flag = true;
			switch(head.op)
			{
				case '+' : c.num = a + b; break;
				case '-' : c.num = a - b; break;
				case '*' : c.num = a * b; break;
				case '/' : c.num = a / b; break;
			}
			S.push(c);
		}
	}
	return S.top().num;
}

int main()
{
	freopen("data.in","r",stdin);
	OP['+'] = OP['-'] = 1;
	OP['*'] = OP['/'] = 2;
	while (true)
	{
		getline(cin,str);
		if (str == "0") break;
		for (string :: iterator it = str.begin(); it != str.end();it ++) 
			if (*it == ' ') str.erase(it);
		while (! S.empty()) S.pop();
		InfixToPostfix();
		printf("%.2f\n",CalcPostfix());
	}	
}

手写栈队列 version

#include <bits/stdc++.h>

using namespace std;

const int N = 100005;

struct node
{
	double num;
	char op;
	bool flag; // true -> number false -> op
};

string str;
node Stack[N];
int top = 0;
//stack <node> S; //op stack
//queue <node> Q; //Postfix Expression
node Queue[N];
map<char,int> OP;
int l = 0 , r = 0;

void InfixToPostfix()
{
	node a;
	for (int i = 0;i < str.length();)
	{
		if (str[i] >= '0' && str[i] <= '9')
		{
			a.flag = true;
			a.num = str[i ++] - '0';
			while (i < str.length() && str[i] >= '0' && str[i] <= '9')
			{
				a.num = a.num * 10 + (str[i] - '0');
				i ++;
			} 	
			Queue[++ r] = a;
		}	
		else
		{
			a.flag = false;
			while (top > 0 && OP[str[i]] <= OP[Stack[top].op]) 
			{
				Queue[++ r] = Stack[top --];
			}
			a.op = str[i];
			Stack[++ top] = a;
			i ++;
		}
	}
	while (top > 0)
	{
		Queue[++ r] = Stack[top --];
	}
}

double CalcPostfix()
{
	double a,b;
	node head,c;
	//while (!Q.empty())
	while (l <= r)
	{
		head = Queue[l ++];
	//	head = Q.front();
	//	Q.pop();
		if (head.flag == true) Stack[++ top] = head; 
		//S.push(head);
		else
		{
			b = Stack[top--].num;
			a = Stack[top--].num; 
			//b = S.top().num;
			//S.pop();
			//a = S.top().num;
			//S.pop();
			c.flag = true;
			switch(head.op)
			{
				case '+' : c.num = a + b; break;
				case '-' : c.num = a - b; break;
				case '*' : c.num = a * b; break;
				case '/' : c.num = a / b; break;
			}
			Stack[++ top] = c;
		//	S.push(c);
		}
	}
	return Stack[top].num;
	//return S.top().num;
}

int main()
{
	freopen("data.in","r",stdin);
	OP['+'] = OP['-'] = 1;
	OP['*'] = OP['/'] = 2;
	while (true)
	{
		getline(cin,str);
		if (str == "0") break;
		for (string :: iterator it = str.begin(); it != str.end();it ++) 
			if (*it == ' ') str.erase(it);
		top = 0;
		//while (! S.empty()) S.pop();
		InfixToPostfix();
		printf("%.2f\n",CalcPostfix());
	}	
}

这里在贴一下手写栈和队列的模板

手写栈

struct stack
{
	const int N = 100000 + 100;
    int a[N], top = 0;
    void push(int x)
    {
        a[++ top] = x;
    }
    int front()
    {
        return a[top];
    }
    void pop()
    {
        top--;
    }
    int empty()
    {
        return top >= 0 ? 1 : 0;
    }
}Stack;

手写队列

struct queue
{
	const int N = 100000 + 100;
    int l = 0,r = 0,a[N];
    void push(int x)
    {
        a[++r] = x;
    }
    int front()
    {
        return a[l];
    }
    void pop()
    {
        l++;
    }
    int empty()
    {
        return l > r ? 1 : 0;
    }
}q;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值