数据结构—栈的应用举例【算术表达式求值转换、后缀表达式求值计算】

简介: 数据结构—栈的应用举例【算术表达式求值转换、后缀表达式求值计算】

栈的应用

     带你走进Java后缀表达式的世界!

e424395976104fa4b72237fa20dd0628.png

           表达式求值问题:编程实现算术表达式求值。


一、什么是算术表达式?表达式又分为那3种表示形式?

                     是由操作数、算术运算符和分隔符所组成的式子。


                     表达式一般有中缀表达式,后缀表达式和前缀表达式共3种表示形式。


                              中缀表达式:是将运算符放在两个操作数的中间。日常生活中编写的加减乘除都是中序表达式。


                             后缀表达式:也称逆波兰表达式,是将运算符放在两个操作数之后。


                             前缀表达式:是将运算符放在两个操作数之前。


------------------------------------------------------------------------


          二、请举例说明一下表达式的三种形式?

例:


中缀表达式:A+(B-C/D) * E


后缀表达式:ABCD/-E*+


前缀表达式:+A *- B/CDE


        三、计算表达式的值采用哪种表达形式好呢?


                                        由于运算符又优先级,所以中缀表达式在描述时,对计算非常不方便,特别是待括号的更麻烦。


                                        而后缀表达式中既无运算符的优先级又无括号的约束问题。在后缀表达式中运算符出现的顺序正是计算的顺序。故而使用后缀表达式更好计数。


------------------------------------------------------------------------


             四、求算术表达式值的步骤分那几步?


                                  求算术表达式的值可分为两步来进行:


                                                       第一步:将原算术表达式换成后缀表达式。


                                                       第二步:再对后缀表达式进行求值。


                       第一步:将原算术表达式转换成后缀表达式:

                                       后缀表达式中的操作数和原算术表达式的先后次序一样,只是运算符的次序不一样;则只需将转换的重点放在运算符的处理上即可。


                                       运算符的优先级表:


937b8676fd504d9e9d259a324749822d.png


注:优先级别从高到低依次用0~3的数字来表示,数字越大,表示其运算符的优先级越高。


------------------------------------------------------------------------


         五、运算符栈


                             使用一个栈来记录保留还未送往后缀表达式的运算符,称为运算符栈。


                                         入栈:新入栈的运算符比栈里的运算符优先级要高。如果是‘(’进行入栈操作。


                                         出栈:新入栈的运算符没有栈里的运算符优先级高,栈里的运算符需要出栈。如果是‘)’出栈直到‘(’


例:已知中序表达式为 8-(3+2*6)/ 5 + 2 ^ 2 ; 求后缀表达式?


解析:


12557c9214dd425eae454490a6eba3c8.png


------------------------------------------------------------------------


     六、实现转换的基本思路和规则!


                       (1) 初始化一个运算符栈


                       (2) 从算术表达式输入的字符串,从左到右读取一个字符。


                       (3) 若当前字符是操作数,则直接送往后缀表达式。


                       (4) 若当前字符是左括号“ ( ” 时,将其进行压栈到运算符栈


                        (5) 若当前字符为运算符时,则:


                                       1.当运算符栈空时,进行压栈。


                                       2.当此运算符的优先级高于栈顶运算符时,进行压栈。反之,弹出栈顶运算符送往后缀式,并将当前运算符压栈,重复步骤(5)


                        (6) 若当前字符为右括号“ )”时,反复将栈顶符号弹出,并送往后缀表达式,直到栈顶符为左括号“( ” 为止,在将左括号出栈并丢弃。


                       (7) 若还未读取完毕,则跳转到(2)


                       (8) 读取完毕,将栈这剩余的所有运算符弹出并送往后缀表达式。


ea0bb2ef720d4f37b41563e11469355b.png


      七、计算后缀表达式值的算法的基本思路!


                 第二步:计算后缀表达式的值。


                                步骤:先找到运算符——》在找前面最后出现的两个操作数——》构成一个最小算术表达式——》 一个栈来保留后缀表达式中还未参与运算的操作数【操作数栈】


                       基本思路:


                                (1) 初始化一个操作数栈


                               (2) 从左到右依次读入后缀表达式中的字符。


                                               1. 若当前字符是操作数,则压栈。


                                                2. 若当前字符是运算符,则从栈顶弹出两个操作数并分别作为第2个操作数和第1个操作数参与运算,在将运算结果进行压栈操作。


                                (3) 重复步骤(2) 直到读入的后缀表达式结束为止。即操作数栈中的栈顶元素为表达式的计算结果。


------------------------------------------------------------------------


        八、程序代码实现后缀表达式计算其值!

                        1. 链栈类【LinkStack】

package data.linear_table.stack_queue;
import data.linear_table.node.Node;
//链栈类
public class LinkStack implements IStack {
    private Node top ;      //栈顶元素的引用
    //将栈置空
    @Override
    public void clear() {
        top = null ;
    }
    //判断链栈是否为空
    @Override
    public boolean isEmpty() {
        return top == null ;
    }
    //求链栈的长度
    @Override
    public int length() {
        Node p = top ;      //初始化,p指向栈顶元素
        int length = 0 ;    //length为计数器
        //从栈顶元素开始向后查找,直到p为空
        while (p != null ) {
            p = p.next ;    //p指向后继结点
            length++ ;      //长度增加1
        }
        return length ;
    }
    //取栈顶元素并返回其值
    @Override
    public Object peek() {
        //栈如果不为空
        if (!isEmpty()) {
            //返回栈顶元素
            return top.data ;
        }else {
            return null ;
        }
    }
    //入栈
    @Override
    public void push(Object x) throws Exception {
        //构造一个新结点
        Node p = new Node(x) ;
        p.next = top ;          //p的指针域指向栈顶元素
        top = p ;               //新结点成为当前的栈顶元素
    }
    //出栈
    @Override
    public Object pop() {
        if (isEmpty()) {
            return null ;
        }else {
            Node p = top ;          //p指向被删除的结点(栈顶元素)。 用于记录被删除的结点,以便返回删除的值
            top = top.next ;        //修改链指针,使栈顶结点从链栈中移去
            return p.data ;         //返回栈顶结点的数据域值
        }
    }
    //输出栈中所有数据元素(从栈顶元素到栈底元素)
    public void display() {
        Node p = top ;          //初始化,p指向栈顶元素
        while (p != null ) {    //输出所有非空结点的数据元素值
            System.out.print(p.data.toString()+" ");
            p = p.next ;        //p指针向后移
        }
    }
}

2. 后缀表达式,计算值类 【Example3】

                          类中方法如下:

                 1、 将算术表达式转换为后缀表达式的函数,结果以字符串的形式进行返回

package data.linear_table.test;
import data.linear_table.stack_queue.LinkStack;
//将算术表达式转换为后缀表达式的函数,结果以字符串的形式进行返回
    //expression 参数为要转换的算术表达式
    public String convertToPostFix(String expression) throws Exception {
        LinkStack st = new LinkStack();     //初始化一个运算符栈
        String postfix = new String();     //用于存放输出的后缀表达式
        for (int i = 0; expression != null && i < expression.length(); i++) {
            char c = expression.charAt(i);      //从算术表达式中读取一个字符
            if (c != ' ') {         //判断c不为空格
                if (isOpenParenthesis(c)) {
                    //为开括号 '( ',压栈
                    st.push(c);
                } else if (isCloseParenthesis(c)) {
                    //为闭括号 ' ) ' ,弹出
                    char ac = (Character) st.pop();         //强制类型转换,弹出栈顶元素;
                    while (!isOpenParenthesis(ac)) {       //循环一直到ac为开括号 ( 为止
                        postfix = postfix.concat(String.valueOf(ac));       //串联到后缀表达式的结尾
                        ac = (Character) st.pop();          //继续弹出元素
                    }
                } else if (isOperator(c)) {
                    //为运算符时
                    if (!st.isEmpty()) {       //判断栈非空时,取出栈顶优先级高的运算符送往后缀表达式
                        Character ac = (Character) st.pop();
                        while (ac!=null && priority(ac.charValue()) >= priority(c)) {
                            postfix = postfix.concat(String.valueOf(ac));   //串联到后缀表达式的结尾
                            ac = (Character) st.pop();         //继续弹出
                        }
                        if (ac != null) {    //若最后一次取出的优先级低的操作符,重新压栈
                            st.push(ac);
                        }
                    }
                    st.push(c);  //压栈
                }else {
                    //为操作数
                    postfix = postfix.concat(String.valueOf(c));       //串联到后缀表达式的结尾
                }
            }
        }
        while (! st.isEmpty()) {        //栈中剩余所有的操作符,串联到后缀表达式结尾
            postfix = postfix.concat(String.valueOf(st.pop())) ;
        }
        return postfix ;
    }

 2、判断字符串是否为运算符

 //判断字符串是否为运算符
    public boolean isOperator(char c) {
        //判断c是否等于 +-*/^ %
        if ('+' == c || '-' == c || '*' == c || '/' == c || '^' == c || '%' == c) {
            return true ;
        }else {
            return false ;
        }
    }

     3、判断字符串是否为开括号 ’ (  ‘

 //判断字符串是否为运算符
    public boolean isOperator(char c) {
        //判断c是否等于 +-*/^ %
        if ('+' == c || '-' == c || '*' == c || '/' == c || '^' == c || '%' == c) {
            return true ;
        }else {
            return false ;
        }
    }

3、判断字符串是否为开括号 ’ (  ‘

 //判断字符串是否为开括号
    public boolean isOpenParenthesis (char c) {
        return '(' == c ;
    }

  4、判断字符串是否为闭括号’  )‘

 //判断字符串是否为闭括号
    public boolean isCloseParenthesis(char c) {
        return ')' == c ;
    }

  5、判断运算法的优先级

   //判断运算法的优先级
    public int priority(char c) {
        if (c == '^') {     //为幂运算
            return 3 ;
        }
        if (c == '*' || c == '/' || c == '%' ) {        //为乘除,取模运算
            return 2 ;
        }else  if (c == '+' || c == '-') {              //为加减运算
            return 1 ;
        }else {                                         //其他运算
            return 0 ;
        }
    }
}

 6、对后缀表达式进行求值计算

//对后缀表达式进行求值计算
    public double numberCalculate(String postfix) throws Exception {
        LinkStack st = new LinkStack();             //初始化一个操作数栈
        for (int i = 0 ; postfix != null && i < postfix.length(); i++ ) {
            char c = postfix.charAt(i);             //从后缀表达式中读取第一个字符
            //当c为操作符时
            if (isOperator(c)) {
                //取出两个操作数
                //st.pop出栈,将前面的两个元素取出
                double d2 = Double.valueOf(st.pop().toString());
                double d1 = Double.valueOf(st.pop().toString());
                double d3 = 0 ;
                if ('+' == c) {         //加法运算
                    d3 = d1 + d2 ;
                }else if ('-' == c) {   //减法运算
                    d3 = d1 -d2 ;
                }else if ('*' == c ) {      //乘法运算
                    d3 = d1 * d2 ;
                }else if ('/' == c ) {      //除法运算
                    d3 = d1 / d2 ;
                }else if ('^' == c ) {      //幂运算
                    d3 = Math.pow(d1,d2);
                }else if ('%' == c ) {      //取模
                    d3 = d1 % d2 ;
                }
                //将处理结果,压栈到操作数栈
                st.push(d3);
            }else {
                //为操作数时,直接压栈
                st.push(c);
            }
        }
        //返回运算结果,栈顶出栈
        return (Double) st.pop();
    }

3. 测试类【Example3_Text】

package data.linear_table.test;
public class Example3_Text {
    public static void main(String[] args) throws Exception {
        //创建后缀表达式,计算值类
        Example3 p = new Example3();
        //调用方法转换为后缀表达式
        String postfix = p.convertToPostFix("(1+2)*(5-2)/2^2+5%3");
        System.out.println("转换的后缀表达式为:"+postfix);
        //对后缀表达式求值,并输出
        System.out.println("计算后缀表达式的结果为:" + p.numberCalculate(postfix));
    }
}

4. 测试结果

40bfe61ec183488abfcea71de856b90c.png

相关文章
|
14天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
90 9
|
5天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
14 1
|
8天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
11天前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
13天前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
40 4
|
1月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
30 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
17天前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
1月前
初步认识栈和队列
初步认识栈和队列
58 10
|
30天前
数据结构(栈与列队)
数据结构(栈与列队)
17 1
|
1月前
|
算法
数据结构与算法二:栈、前缀、中缀、后缀表达式、中缀表达式转换为后缀表达式
这篇文章讲解了栈的基本概念及其应用,并详细介绍了中缀表达式转换为后缀表达式的算法和实现步骤。
44 3

热门文章

最新文章