波兰式即为我们日常使用的式子,也称为中缀表达式,像是平时的1+1
而逆波兰式又名后缀表达式,后缀表达式比中缀表达式计算起来更方便简单些,中缀表达式要计算就存在着括号的匹配问题,所以在计算表达式值时一般都是先转换成后缀表达式,再用后缀法计算表达式的值
逆波兰式的作用
实现逆波兰式的算法,难度并不大,但为什么要将看似简单的中缀表达式转换为复杂的逆波兰式?原因就在于这个简单是相对人类的思维结构来说的,对计算机而言中序表达式是非常复杂的结构。相对的,逆波兰式在计算机看来却是比较简单易懂的结构。因为计算机普遍采用的内存结构是栈式结构,它执行先进后出的顺序。
调度场算法
调度场算法(Shunting Yard Algorithm)是一个用于将中缀表达式转换为后缀表达式的经典算法,由艾兹格·迪杰斯特拉引入,因其操作类似于火车编组场而得名。
java版本:
如下是一个应用调度场算法将波兰式转为逆波兰式的代码
package jisuan;
import java.util.Stack;
import java.util.regex.Pattern;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Font;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Scanner;
public class yunsuan{
JFrame jf;
JPanel jp;
JTextField jtf1,jtf2,jtf3,jtf4;
public String changeFix(String fixExpression){
Stack<Character> stack = new Stack<>();
String postfixExpression = "";
for(int i=0; i<fixExpression.length(); i++){
char c = fixExpression.charAt(i);
char top;
switch (c) {
case ' ':
break;
case '+':
case '-':
while (!stack.isEmpty())
{
top = stack.pop();
if (top == '(') {
stack.push('(');
break;
}
postfixExpression += " " + top;
}
stack.push(c);
postfixExpression += " ";
break;
case '*':
case '/':
while (!stack.isEmpty()){
top = stack.pop();
if (top == '(' || top == '+' || top == '-')
{
stack.push(top);
break;
}
else
postfixExpression += " " + top;
}
stack.push(c);
postfixExpression += " ";
break;
case '(':
stack.push(c);
break;
case ')':
while(!stack.isEmpty()) {
top = stack.pop();
if (top == '(')
break;
else
postfixExpression += " " + top;
}
break;
default:
postfixExpression += c;
}
}
while(!stack.isEmpty()) {
postfixExpression += " " + stack.pop();
}
return postfixExpression;
}
public double evalRPN(String postfixExpression){
Stack<Double> stack = new Stack<>();
Pattern pattern = Pattern.compile("\\d+||(\\d+\\.\\d+)");//Pattern.compile方法截取整数和小数
String strings[] = postfixExpression.split(" "); //将字符串转化为字符串数组
for (int i = 0; i < strings.length; i++) {
strings[i].trim(); //trim方法裁剪空格
}
for (int i = 0; i < strings.length; i++)
{
if ((pattern.matcher(strings[i])).matches())//识别数字
{
stack.push(Double.parseDouble(strings[i]));
}
else{
double d1 = stack.pop();
double d2 = stack.pop();
stack.push(caculate(d1, d2, strings[i]));
}
}
return stack.pop();
}
public static double caculate(double d1, double d2, String simple){
if (simple.trim().equals("+"))
return d1 + d2;
if (simple.trim().equals("-"))
return d2 - d1;
if (simple.trim().equals("*"))
return d1 * d2;
if (simple.trim().equals("/"))
return d2 / d1;
return 0;
}
public void ceshib(String a) {
jf = new JFrame("表达式运算器");
Container contentPane = jf.getContentPane();
contentPane.setLayout(new BorderLayout());
jp = new JPanel();
jtf1 = new JTextField();
jtf2 = new JTextField(10);
jtf3 = new JTextField("答案为:");
jtf4 = new JTextField(a,30);
jtf3.setFont(new Font("谐体",Font.BOLD|Font.ITALIC,16));
jtf4.setFont(new Font("谐体",Font.BOLD|Font.ITALIC,16));
//设置文本的水平对齐方式
jtf4.setHorizontalAlignment(JTextField.CENTER);
jp.add(jtf3);
jp.add(jtf4);
contentPane.add(jp);
jf.pack();
jf.setLocation(400, 200);
jf.setVisible(true);
jf.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
yunsuan rpn = new yunsuan();
System.out.println("请输入:");
String s1 = scan.next();
String s2 = rpn.changeFix(s1);
double end = rpn.evalRPN(s2);
String a=end+"";
rpn.ceshib(a);
}
}
表达式求值,完成算法描述部分和编码实现部分。
问题描述:计算用运算符后缀法表示的表达式的值。后缀表达式也称逆波兰表达式,比中缀表达式计算起来更方便简单些,中缀表达式要计算就存在着括号的匹配问题,所以在计算表达式值时一般都是先转换成后缀表达式,再用后缀法计算表达式的值。如:表达式(a+bc)/d-e 用后缀法表示应为 abc+d/e-。只考虑四则算术运算,且假设输入的操作数均为 1 位十进制数(0—9),并且输入的后缀形式表达式不含语法错误。
C语言版本:
#include
#include
#include
#include
#define add 43 /*运算符加号‘+’的 ASCII 码*/
#define subs 45 /*运算符减号‘-’的 ASCII 码*/
#define mult 42 /*运算符乘号‘*’的 ASCII 码*/
#define div 47 /*运算符除号‘/’的 ASCII 码*/
#define MAXSIZE 100
//栈的数组实现
typedef struct
{
int stkdata[MAXSIZE];
int top;
}Stack;
//为栈分配空间
Stack* Createstack()
{
Stack* p;
p = (Stack*)malloc(sizeof(*p));
p->top = -1;
return p;
}
//压栈
int Push(Stack* p, int x)
{
if (p->top == MAXSIZE - 1)
{
return -1;
}
p->top++;
p->stkdata[p->top] = x;
return 0;
}
//出栈
int Pop(Stack* L, int* x)
{
if (L->top == -1)
{
return -1;
}
//利用传出参数传出栈顶元素
*x = L->stkdata[L->top];
L->top--;
return 0;
}
//栈顶
int TOP(Stack* L, int* x)
{
if (L->top == -1)
{
return -1;
}
*x = L->stkdata[L->top];
return 0;
}
//判断栈是否为空
int Empty(Stack* L)
{
return (L->top == -1);
}
//定义符号的优先级
int Priority(int ope)
{
switch (ope)
{
case '(': return 0; //左括号已经在栈内时,如果比较,其优先级最低
case '+':
case '-': return 1;
case '*':
case '%':
case '/': return 2;
case '^': return 3;
default: return -1;
}
}
// 将两个数出栈、根据ope符号计算,然后再次入栈
void Calculation(Stack* snum, int ope)
{
int n, n1, n2;
Pop(snum, &n1);
Pop(snum, &n2);
switch (ope)
{
case '+': n = n1 + n2; break;
case '-': n = n2 - n1; break;
case '*': n = n1 * n2; break;
case '/': n = n2 / n1; break;
case '%': n = n2 % n1; break;
case '^': n = pow(n2, n1);break;
}
Push(snum, n);
}
// 先处理除右括号外的符号
void Deal_ope(Stack* snum, Stack* sope, int ope)
{
int old_ope;
if (Empty(sope) || ope == '(')
{
Push(sope, ope);
return;
}
TOP(sope, &old_ope);
if (Priority(ope) > Priority(old_ope))
{
//传入符号大于当前栈顶,则将传入符号入栈
Push(sope, ope);
return;
}
//如果传入的符号优先级小于当前栈顶符号
while (Priority(ope) <= Priority(old_ope))
{
//将当前栈顶的符号取出与数字栈中顶端的两个数字进行计算
Pop(sope, &old_ope);
printf("%c ", old_ope);
Calculation(snum, old_ope);
if (Empty(sope))
{
break;
}
//再次取出一个当前栈符号与传入符号比较,循环
TOP(sope, &old_ope);
}
Push(sope, ope);
}
//单独处理右括号
void Right(Stack* snum, Stack* sope)
{
int old_ope;
TOP(sope, &old_ope);
while (old_ope != '(')
{
//当前符号出栈然后将数字出栈两个进行计算,在括号内优先级最高
Pop(sope, &old_ope);
printf("%c ", old_ope);
Calculation(snum, old_ope);
//循环
TOP(sope, &old_ope);
}
Pop(sope, &old_ope);//出现左括号时将它丢弃
}
// 打印数字栈
void Display(Stack* L)
{
int i;
if (L->top == -1)
{
return;
}
for (i = 0; i <= L->top; i++)
{
printf("%d ", L->stkdata[i]);
}
printf("\n");
}
//打印符号栈
void Displayope(Stack* L)
{
int i;
if (L->top == -1)
{
return;
}
for (i = 0; i <= L->top; i++)
{
printf("%c ", L->stkdata[i]);
}
printf("\n");
}
/*从文件中读入中缀表达式*/
void Readexpression(char a[])
{
char x[MAXSIZE];
int i = 1, j = 0;
gets_s(x);
for (i = 1;x[i] != '#';i++)
{
a[j] = x[i];
j++;
}
a[j] = '\0';
}
int main()
{
char str[MAXSIZE], dst[MAXSIZE];
printf("中缀表达式为:");
Readexpression(str);
printf("%s\n", str);
int i = 0, value = 0, flag = 0; //数字的值
int old_ope;
Stack* numstack, * opestack;
numstack = Createstack(); // 创建存放数字的栈
opestack = Createstack(); // 创建存放运算符的栈
printf("后缀表达式为:");
//printf("栈的变化如下:\n");
/* 表达式字符串解析函数,然后将高优先级的符号/(*)进行计算重新入栈
退出while大家的优先级都一样*/
while (str[i] != '\0')
{
if (str[i] >= '0' && str[i] <= '9')
{
value *= 10; //数字的获取,注意可能不止一位
value += str[i] - '0';
flag = 1;
}
else
{
if (flag) //flag = 1说明value里面存储了数字,将其入栈
{
printf("%d ", value);
Push(numstack, value);
//flag标志清零,value存放数字的变量清零
flag = 0;
value = 0;
//Display(numstack);
}
if (str[i] == ')')
{
Right(numstack, opestack);
//Displayope(opestack);
}
else
{
Deal_ope(numstack, opestack, str[i]);
//Displayope(opestack);
}
}
i++;
}
if (flag) //如果flag = 1.说明value里面还有数值,将其入栈
{
printf("%d ", value);
Push(numstack, value);
//Display(numstack);
}
while (!Empty(opestack)) //如果符号栈不为空,继续计算
{
Pop(opestack, &old_ope);
printf("%c ", old_ope);
Calculation(numstack, old_ope);
//Displayope(opestack);
}
Pop(numstack, &value); //最终答案
printf("\n%s = %d\n", str, value);
return 0;
}