前缀(波兰表达式),中缀表达式,后缀表达式(逆波兰表达式)

前缀表达式的计算机求值

从右至左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素 和 次顶元素),并将结果入栈;重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果

例如: (3+4)×5-6 对应的前缀表达式就是 - × + 3 4 5 6 , 针对前缀表达式求值步骤如下:

1) 右至左扫描 ,将 6 5 4 3 压入堆栈
2) 遇到 + 运算符,因此弹出 3 4 3 为栈顶元素, 4 为次顶元素),计算出 3+4 的值,得 7 ,再将 7 入栈
3) 接下来是 × 运算符,因此弹出 7 5 ,计算出 7×5=35 ,将 35 入栈
4) 最后是 - 运算符,计算出 35-6 的值,即 29 ,由此得出最终结果

中缀表达式

1) 缀表达式就是 常见的运算表达式 ,如 (3+4)× 5-6
2) 中缀表达式的求值是我们人最熟悉的,但是对计算机来说却不好操作 ( 前面我们讲的案例就能看的这个问题 ) ,因此,在计算结果时,往往会将中缀表达式转成其它表达式来操作 ( 一般转成后缀表达式 .)
 

表达式的计算机求值

左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(次顶元素 和 栈顶元素),并将结果入栈;重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果

例如: (3+4)×5-6 对应表达式就是 3 4 + 5 × 6 - , 针对缀表达式求值步骤:

1) 左至右扫描,将 3 4 压入堆栈
2) + 运算符,因此弹出 4 3 4 为栈顶元素, 3 为次顶元 素), 计算出 3+4 的值,得 7 ,再将 7 入栈
3) 5 入栈
4) 下来是 × 运算符,因此弹出 5 7 ,计算出 7×5=35 ,将 35 入栈
5) 6 入栈
后是 - 运算符,计算出 35-6 的值,即 29 ,由此得出最终结
public class PolandNotation {
    public static void main(String[] args) {
        //先定义一个逆波兰表达式
        //(3+4)*5-6 =>3 4 + 5 * 6-
        //说明,为了方便,逆波兰表达式的数字和符号使用空格隔开
        String suffixExpression = "30 4 + 5 * 6 -";
        //思路
        //1.先将“3 4 + 5 * 6-”放入ArrayList中
        //2.将ArrayList传递给一个方法,遍历ArrayList配合栈,完成计算

        List listString = getListString(suffixExpression);
        System.out.println(listString);
        System.out.println(calculate(listString));
    }

    //将一个逆波兰表达式,依次将数据和运算符放入到ArrayList中
    public static List getListString(String suffixExpression){
        String[] split = suffixExpression.split(" ");
        ArrayList list = new ArrayList();
        for (String s : split) {
            list.add(s);
        }
        return list;
    }

    //计算
    public static int calculate(List ls){
        Stack stack = new Stack();
        for (String item : ls) {
            //使用正则表达式来取出数
            if(item.matches("\\d+")){//匹配的是多位数
                stack.push(item);
            }else{
                //pop出两个数,并运算,再入栈
                int num2 = Integer.parseInt(stack.pop());
                int num1 = Integer.parseInt(stack.pop());
                int res = 0;
                if(item.equals("+")){
                    res = num1 + num2;
                }else if(item.equals("-")){
                    res = num1 - num2;
                }else if(item.equals("*")){
                    res = num1 * num2;
                }else if(item.equals("/")){
                    res = num1 / num2;
                }else{
                    throw new RuntimeException("运算符有误");
                }
                //把res入栈
                stack.push(res +"");
            }
        }
        //最后留在stack中的数据是运算结果
        return Integer.parseInt(stack.pop());
    }
}

以上是逆波兰实现计算器计算

l 缀表达式转换为后缀表达式
 

大家看到,后缀表达式适合计算式进行运算,但是人却不太容易写出来,尤其是表达式很长的情况下,因此在开发中,我们需要将 缀表达式转成后缀表达式

体步骤如下:

1) 始化两个栈:运算符栈 s1 和储存中间结果的栈 s2
2) 左至右扫描中缀表达式
3) 到操作数时,将其压 s2
4) 到运算符时,比较其与 s1 栈顶运算符的优先级:
(1) s1 为空,或栈顶运算符为左括号“ (” ,则直接将此运算符入栈
(2) 则,若优先级比栈顶运算符的高,也将运算符压入 s1

则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4-1)s1中新的栈顶运算符相比较

5) 到括号时
(1) 果是左括号“ (” ,则直接压入 s1
(2)
果是右括号“ )” ,则依次弹出 s1 栈顶的运算符,并压入 s2 ,直到遇到左括号为止,此时将这一对括号丢弃
6) 复步骤 2 5 ,直到表达式的最右
7) s1 中剩余的运算符依次弹出并压入 s2
8) 依次弹出 s2 中的元素并输出, 结果的逆序即为中缀表达式对应的后缀表达
 

 前缀(波兰表达式),中缀表达式,后缀表达式(逆波兰表达式)_第1张图片

代码实现中缀表达式转后缀表达式: 

public class PolandNotation {
    public static void main(String[] args) {

        //完成将一个中缀表达式转成后缀表达式的功能
        //说明
        //1. 1+((2+3)×4)-5 => 1 2 3 + 4 × + 5 –
        //2.因为直接对字符串操作不方便,先将字符串转成中缀表达式对应的list
        //3.将得到的中缀表达式对应的list转成后缀表达式对应的list
        String expression ="1+((2+3)*4)-5";
        //将中缀表达式转成对应的List
        List infixExpressionList = toInfixExpressionList(expression);
        System.out.println(infixExpressionList);
        //将中缀表达式的list转成后缀表达式
        List list = parseSuffixExpressionList(infixExpressionList);
        System.out.println(list);
        //根据后缀表达式计算最终值
        System.out.println(calculate(list));

        //先定义一个逆波兰表达式
        //(3+4)*5-6 =>3 4 + 5 * 6-
        //说明,为了方便,逆波兰表达式的数字和符号使用空格隔开
//        String suffixExpression = "30 4 + 5 * 6 -";
//        //思路
//        //1.先将“3 4 + 5 * 6-”放入ArrayList中
//        //2.将ArrayList传递给一个方法,遍历ArrayList配合栈,完成计算
//
//        List listString = getListString(suffixExpression);
//        System.out.println(listString);
//        System.out.println(calculate(listString));
    }

    //将中缀表达式转成后缀表达式
    public static List parseSuffixExpressionList(List ls){
        //定义两个栈
        Stack s1 = new Stack();//符号栈
        //说明:因为s2这个栈,在整个转换过程中,没有pop操作,而且后面我们还需要逆序输出,比较麻烦,因此直接使用list
        List s2 = new ArrayList();

        //遍历ls
        for (String item : ls) {
            //如果是一个数,就加入到s2
            if(item.matches("\\d+")){
                s2.add(item);
            }else if(item.equals("(")){
                s1.push(item);
            }else if(item.equals(")")){
//                如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
                while (!s1.peek().equals("(")){
                    s2.add(s1.pop());
                }
                s1.pop();//将(弹出,消除小括号
            }else{
                //当item的优先级小于等于栈顶运算符的优先级,将s1栈顶的运算符弹出并压入到s2中,再次转到(4-1)与s1中新的栈顶运算符相比较;
                //问题:我们缺少一个比较优先级高低的方法
                while(s1.size()!=0 && Operation.getValue(s1.peek())>=Operation.getValue(item)){
                    s2.add(s1.pop());
                }
                //还需要将item压入栈
                s1.push(item);
            }
        }
        //将s1中剩余的运算符一次弹出并加入s2
        while(s1.size()!=0){
            s2.add(s1.pop());
        }
        return s2;//因为存在list,因此顺序输出就是栈逆序输出
    }

    //将中缀表达式转成对应的List
    public static List toInfixExpressionList(String s){
        //定义一个list,存放中缀表达式对应的内容
        ArrayList ls = new ArrayList();
        int i=0;//这是一个指针,用于遍历中缀表达式字符串
        String str;//对多位数的拼接
        char c;//没遍历到一个字符,就放入到c
        do {
            //如果c是一个非数字,需要加入到ls
            if((c=s.charAt(i))<48 ||(c=s.charAt(i))>57){
                ls.add(""+c);
                i++;
            }else{//如果是一个数,需要考虑多位数
                str ="";//先将str置空,'0'[]48-'9'[57]
                while(i=48 &&(c=s.charAt(i))<=57){
                    str +=c;
                    i++;
                }
                ls.add(str);
            }
        }while (i getListString(String suffixExpression){
        String[] split = suffixExpression.split(" ");
        ArrayList list = new ArrayList();
        for (String s : split) {
            list.add(s);
        }
        return list;
    }

    //根据后缀表达式计算值
    public static int calculate(List ls){
        Stack stack = new Stack();
        for (String item : ls) {
            //使用正则表达式来取出数
            if(item.matches("\\d+")){//匹配的是多位数
                stack.push(item);
            }else{
                //pop出两个数,并运算,再入栈
                int num2 = Integer.parseInt(stack.pop());
                int num1 = Integer.parseInt(stack.pop());
                int res = 0;
                if(item.equals("+")){
                    res = num1 + num2;
                }else if(item.equals("-")){
                    res = num1 - num2;
                }else if(item.equals("*")){
                    res = num1 * num2;
                }else if(item.equals("/")){
                    res = num1 / num2;
                }else{
                    throw new RuntimeException("运算符有误");
                }
                //把res入栈
                stack.push(res +"");
            }
        }
        //最后留在stack中的数据是运算结果
        return Integer.parseInt(stack.pop());
    }
}

//编写一个类Operation可以返回一个运算符对应的优先级
class Operation{
    private static int ADD = 1;
    private static int SUB = 1;
    private static int MUL = 2;
    private static int DIV = 2;

    //写一个方法返回对应的优先级数字
    public static int getValue(String operation){
        int result = 0;
        if(operation.equals("+")){
            result = ADD;
        }else if(operation.equals("-")){
            result = SUB;
        }else if(operation.equals("*")){
            result = MUL;
        }else if(operation.equals("/")){
            result = DIV;
        }else {
            System.out.println("不存在该运算符");
        }
        return result;
    }
}

 

你可能感兴趣的:(数据结构,栈)