C++ | 栈的应用(逆波兰算法) | 计算器

后缀式的特点可用于计算表达式的值,我们通过中缀转后缀的方式可以将原表达式重新排列组合成为计算机易于理解的计算方式。
详情请参考:逆波兰算法(后缀表达式)

关于中缀转后缀

中缀式转后缀式 如:
1+2就是一个中缀式,转换成后缀式为1 2 +
1+2*3…… ……1 2 3 * +
1+(2+5)…… ……1 2 5 + +

在计算时,从最左侧开始先选定一个符号该符号左边的两个数结合运算。以下(黄色高亮部分)计算过程如下

  • 1+2…… ……1 2 +
1与2进行+运算
  • 1+2*3…… ……1 2 3 * +
1. 最左边的符号 * ,左侧的两个数 2 3
		计算:2*3 = 6
		表达式:1 6 +
2.  最左边的符号 + ,左侧的两个数 1 6
		计算:1+6 = 7
  • 1+(2+5)…… ……1 2 5 + +
3. ...
		计算:2+5=7
		表达式:1 7 +
4. ...
 		计算:1+7=8

如上所示,使用后缀式可以清晰的表示出表达式中的运算关系,所有运算符符号中,优先级最高的在最左边。

计算时从最左侧的符号先开始,且每个符号都与它左侧最近的两个数据结合

可以说,后缀式就是根据计算表达式中的优先级进行设计的。
比如,在计算表达式中 ’ ( ) ’ 的优先级最高,因此在转后缀式时遇到 ‘(’ 与 ‘)’ 之间的符号就认为是优先级最高的符号。

eg: a*(b+c)
a b c + *

过程详解:
1、===a*(b+c)===
      ↑
后缀: a               ✔
符号:
2、===a*(b+c)===
       ↑
后缀: a
符号: *               ✔
3、===a*(b+c)===
        ↑
后缀: a
符号: * (             ✔
4、===a*(b+c)===
         ↑
后缀: a b             ✔
符号: * (
5、===a*(b+c)===
          ↑
后缀: a b
符号: * ( +           ✔
6、===a*(b+c)===
           ↑
后缀: a b c           ✔
符号: * ( +
7、===a*(b+c)===
            ↑
后缀: a b c
符号: * ( + )         ✔
注:遇到')',后面进来的符号都视为较低优先级符号,因此把' ( ) '
	内的符号全部转移到后缀式中
8、=============
后缀: a b c +         ✔
符号: *    
9、=============
后缀: a b c + *       ✔
符号:    
中缀转后缀技巧

中缀转后缀的核心就是,将优先级高的运算放在最前边计算,如 … (b+c)… 形式的表达式 ,要变成 … b c + … 。后缀式中优先级体现在符号自左向右的顺序,表达式的关系体现在每个符号与其左边最近的两个数字

而针对表达式 a+b-c ,因为 ‘+’ ‘-’ 优先级相同,而我们的计算顺序为同优先级自左向右,则我们认为左边的 ‘+’ 优先级更高。因此后缀式为 a b + c - ,其中 a b + 为(a+b) 就是原式中优先级高的部分。

而对于 a + b * c 的表达式,显然 b*c 的优先级最高,则后缀式为 a b c * + 。

在本例中,函数 void InfixToSuffix(); 进行中缀转后缀计算。

中缀转后缀计算机代码

#include <iostream>

using std::cin;
using std::cout;
using std::endl;


template<typename T>
class Stack
{
public:
	Stack(int _m_size = 20) :m_size(_m_size), mtop(0)
	{
		data = new T[m_size]();
	}
	~Stack()
	{
		delete[] data;
	}
	/* 判空 */
	bool empty()
	{
		return mtop == 0;
	}
	void push(int val)
	{
		if (Full())
		{
			throw std::exception("error: Stack is Full !");
		}
		data[mtop] = val;
		mtop++;
	}
	T top()
	{
		if (empty())
		{
			throw std::exception("error: The stack is empty !");
		}
		return data[mtop - 1];
	}
	void pop()
	{
		if (empty())
		{
			throw std::exception("error: Stack is Empty !");
		}
		mtop--;
	}
	void show()
	{
		if (empty())
		{
			cout << endl;
		}
		else
		{
			int i = 0;
			while (i < mtop)
			{
				cout << data[i] << " ";
				i++;
			}
			cout << endl;
		}
	}
private:
	bool Full()
	{
		return mtop == m_size;
	}
	T* data;
	int mtop;
	int m_size;
};



/* 计算器类 */
class Calculator
{
public:
	// 这里m_size 默认给的是40,m_size限制表达式的长短
	Calculator(int _m_size = 40) :m_size(_m_size), m_result(0)
	{
		m_str = new char[_m_size + 1]();
	}
	~Calculator()
	{
		delete[] m_str;
	}
	/* 输入 */
	void setData()
	{
		cin.getline(m_str, m_size - 1); 	//最多输入m_size -1个,末尾加'/0'
	}
	/* 中缀转后缀 */
	void InfixToSuffix();		/* 把m_str输入的中缀式转换为后缀式 */
	/* 后缀计算 */
	void Compure();
	/* 计算结果 */
	double Getm_result()
	{
		return m_result;
	}
private:
	bool IsPop(char a, char b);
	char* m_str;
	double m_result;
	int m_size;

};


int main()
{
	while (1) {
		Calculator ca;
		ca.setData();
		ca.InfixToSuffix();
		ca.Compure();
		cout << ca.Getm_result() << endl;

	}
	return 0;
}



// 比较符号优先级,是否出符号栈
// 栈顶元素b的优先级是否高于符号a, true/false  出栈b/入栈a
bool Calculator::IsPop(char a, char b)	//a为待比较符号,b为符号栈栈顶元素
{
	if (b == '(') return false;		//‘(’优先级最低 
	if (a == '*' || a == '/')
	{
		if (b == '+' || b == '-')
		{
			//可以入符号栈  // 栈外优先级高,可以继续入栈
			return false;
		}
		else
		{	
			return true;
		}
	}
	//if (a == '+' || a == '-')
	//{
		//a符号为最低优先级,符号栈出栈
	return true;
	//}
}


/* 中缀转后缀 入栈 */
void Calculator::InfixToSuffix()
{
	// 转换为后缀式后,为了区分数字:如5、4……,在原来的数据增加了分隔符
	// dst 中存储输入的表达式的后缀式形式                size*2
	char* dst = new char[m_size*2]();	
	/* 符号栈 */
	Stack<char> symbol;	// 在转后缀的过程中,(通过符号优先级)控制符号在后缀式中的位置

	int i = 0;
	int k = 0;
	while (m_str[i] != '\0')
	{
		/*----------- 特殊符号判断------------------------------*/
		if (m_str[i] == ' ')		//如果当前字符是空格,则往后走一个
		{
			i++;
			continue;
		}
		else if (m_str[i] == '(')	//左括号直接入栈
		{
			symbol.push(m_str[i]);
		}
		else if (m_str[i] == ')')	//遇到 ) ,输出( )之间的所有符号
		{
			while (symbol.top() != '(')
			{
				dst[k++] = symbol.top();
				dst[k++] = ' ';
				symbol.pop();
			}
			symbol.pop();
		}
		/*----------------- 数字 -------------------------------*/
		else if (isdigit(m_str[i]))
		{
			dst[k++] = m_str[i];
			if (!isdigit(m_str[i + 1]))		//数字后加空格
			{
				dst[k++] = ' ';
			}
		}
		/*----------------- 运算符 -------------------------------*/
		else
		{
			switch (m_str[i])
			{
			case '+':case '-':case '*':case '/':
				if (symbol.empty())		//如果符号栈为空,直接入符号
				{
					symbol.push(m_str[i]);
				}
				else				//否则,判断是否选择入符号栈还是出栈顶元素
				{
					// 栈内符号优先级高于栈外,出栈。 IsPop == true;
					if (IsPop(m_str[i], symbol.top()))	//出符号栈
					{
						dst[k++] = symbol.top();
						symbol.pop();
						continue;
					}
					else			//当前符号优先级高,入符号栈
					{
						symbol.push(m_str[i]);
					}
				}
				break;
			default:
				break;
			}

		}

		i++;	/* 遍历下一字符 */
	}
	/*字符串已遍历完,把符号栈中剩余的符号入栈到数字栈中 */
	while (!symbol.empty())
	{
		dst[k++] = symbol.top();
		symbol.pop();
	}
	dst[k] = '\0';

	// 输出后缀式
	//cout << dst << endl;
	// 后缀表达式 保存到 Calculator::m_str中
	// 交换dst 与 m_str 的堆区空间
	char* tmp = dst;
	dst = m_str;
	m_str = tmp;		
	delete[]dst;
}

void Calculator::Compure()
{
	/* 辅助栈 */
	Stack<double> st;

	int i = 0;
	while (m_str[i] != '\0')
	{
		/*----------- 特殊符号判断------------------------------*/
		if (m_str[i] == ' ')		//如果当前字符是空格,则往后走一个
		{
			i++;
			continue;
		}

		/*----------------- 数字 -------------------------------*/
		else if (isdigit(m_str[i]))
		{
			double tmp = 0;
			while (m_str[i] != ' ')
			{
				tmp = tmp * 10 + m_str[i] - '0';
				i++;
			}
			st.push(tmp);
		}
		/*----------------- 运算符 -------------------------------*/
		else if (!st.empty())		//非空
		{
			double tmp = 0;
			switch (m_str[i])
			{
			case '+':
				tmp += st.top();
				st.pop();
				tmp += st.top();
				st.pop();
				st.push(tmp);
				break;
			case '-':
				tmp -= st.top();
				st.pop();
				tmp += st.top();
				st.pop();
				st.push(tmp);
				break;
			case '*':
				tmp = st.top();
				st.pop();
				tmp *= st.top();
				st.pop();
				st.push(tmp);
				break;
			case '/':
			{
				tmp = st.top();
				st.pop();
				if (tmp != 0)
				{
					tmp = st.top() / tmp;
					st.pop();
					st.push(tmp);
				}
				else
				{
					throw std::exception("error: Divisor of 0 !");
				}
			}
			break;
			default:
				break;
			}

		}

		i++;	/* 遍历下一字符 */
	}

	this->m_result = st.top();
	st.top();
}


运行截图:
在这里插入图片描述

注:在计算器类中

Calculator(int _m_size = 40) :m_size(_m_size), m_result(0)
{}

构造函数 _m_size 默认设置的为40,这将影响到我们输入的计算表达式的长度,我们在编译时可以根据需求自行更改代码。

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
为了保证算法的正确性,我们首先要将中缀表达式化成后缀表达式,也就是逆波兰表达式。实现逆波兰表达式的常用方法是使用栈来实现。 下面是代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #define MAX_EXPR_LEN 100 // 表达式最大长度 #define MAX_STACK_SIZE 50 // 最大长度 // 结构体 typedef struct Stack { int top; int data[MAX_STACK_SIZE]; } Stack; // 初始化 void init(Stack *s) { s->top = -1; } // 入 void push(Stack *s, int data) { s->top++; s->data[s->top] = data; } // 出 int pop(Stack *s) { int data = s->data[s->top]; s->top--; return data; } // 判断是否为空 int isEmpty(Stack *s) { return (s->top == -1); } // 逆波兰表达式求解 int evaluate(char *expr) { Stack s; init(&s); int i, len = strlen(expr); int op1, op2; for (i = 0; i < len; i++) { if (isdigit(expr[i])) { push(&s, expr[i] - '0'); } else if (expr[i] == '+' || expr[i] == '-' || expr[i] == '*' || expr[i] == '/') { op2 = pop(&s); op1 = pop(&s); switch (expr[i]) { case '+': push(&s, op1 + op2); break; case '-': push(&s, op1 - op2); break; case '*': push(&s, op1 * op2); break; case '/': push(&s, op1 / op2); break; default: break; } } } return pop(&s); } int main() { char expr[MAX_EXPR_LEN]; printf("请输入表达式:"); scanf("%s", expr); printf("结果为:%d\n", evaluate(expr)); return 0; } ``` 使用方法: 1. 编译代码:`gcc filename.c -o filename`,filename 为你给代码起的名字 2. 运行代码:`./filename`,根据提示输入表达式 3. 程序将输出表达式的计算结果 注意: 1. 表达式长度不得超过100 2. 表达式中只能包含数字,加减乘除四种运算符 3. 表达式必须符合逆波兰表达式的格式要求 以上就是用 c 的逆波兰算法实现表达式求解的代码实现。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我叫RT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值