逆波兰式的转化与计算(中缀转后缀计算)

逆波兰式的转化与计算


波兰式与逆波兰式

波兰式即为我们日常使用的式子,也称为中缀表达式,像是平时的1+1
而逆波兰式又名后缀表达式,后缀表达式比中缀表达式计算起来更方便简单些,中缀表达式要计算就存在着括号的匹配问题,所以在计算表达式值时一般都是先转换成后缀表达式,再用后缀法计算表达式的值

  • 如果E是一个变量或常量,则E的后缀式是E本身。 如果E是E1 op E2形式的表达式,这里op是任何二元操作符,则E的后缀式为E1’E2’
  • op,这里E1’和E2’分别为E1和E2的后缀式。 如果E是(E1)形式的表达式,则E1的后缀式就是E的后缀式。
  • 如:我们平时写a+b,这是中缀表达式,写成后缀表达式就是:ab+

逆波兰式的作用
实现逆波兰式的算法,难度并不大,但为什么要将看似简单的中缀表达式转换为复杂的逆波兰式?原因就在于这个简单是相对人类的思维结构来说的,对计算机而言中序表达式是非常复杂的结构。相对的,逆波兰式在计算机看来却是比较简单易懂的结构。因为计算机普遍采用的内存结构是栈式结构,它执行先进后出的顺序。

调度场算法
调度场算法(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;
}

你可能感兴趣的:(逆波兰式的转化与计算(中缀转后缀计算))