数据结构(栈)的应用——Stack(实现逆波兰表达式)

栈也是数据结构之一,栈是限定仅在表尾进行插入和删除的线性表。

允许插入和删除的一端我们称为栈顶(top)。另一端称为栈底(bottom),不含任何数据元素的栈称之为空栈。栈有个很重要的特性——后进先出。

一、栈的存储结构

(一)栈的顺序存储结构:

顺序栈的出入栈操作示意图:

(二)栈的链式存储结构:

链栈的出入栈操作示意图:

二、栈的源码分析

1.继承关系

  public  class Stack<E> extends Vector<E>

Vector是向量,基于顺序表实现的,是线程安全的。由此可见jdk中的栈是通过顺序表实现的,如果想要使用链表结构实现的栈可以使用LinkedList,LinkedList实现了栈的方法,也可以自己手动实现栈的链式结构代码。

2.构造方法

  public Stack() {
  }

3.栈的出栈入栈方法

//入栈操作,将元素推入栈顶,也就是顺序表表尾
public E push(E item) {
        addElement(item);
        return item;
    }

//取出栈顶的元素,也就是表尾的元素(取出来后并且删除表中的元素)
public synchronized E pop() {
        E obj;
        int len = size();
        obj = peek();
        removeElementAt(len - 1);  //删除操作
        return obj;
    }

//取出栈顶元素,但是不从栈中删除该元素
public synchronized E peek() {
        int len = size();
        if (len == 0)
            throw new EmptyStackException();
        return elementAt(len - 1);
    }
  1. 栈继承了Vector,所以Stack都可以使用Vector中的非私有的方法。另外它自己定义了两个集合方法.
 //判断栈是否为null,我们一般都是用它父类的isEmpty()方法
public boolean empty() {  
        return size() == 0;
    }

//查找对象在栈中的位置
public synchronized int search(Object o) {
        int i = lastIndexOf(o);

        if (i >= 0) {
            return size() - i;
        }
        return -1;
    }

三、使用栈来实现逆波兰表达式(中缀表达式转变成后缀表达式)

1.定义

中缀表达式就是我们眼中的四则表达式,进行四则运算的式子。
后缀表达式就是计算机识别我们的四则表达式后的样子。

  1. 中缀表达式转变成后缀表达式的规则:

数字输出,运算符进栈,括号匹配出栈,栈顶优先级低出栈。

3.计算机中运算符的优先级关系

e1表示栈顶,e2表示栈底

4.后缀表达式本身的实现算法(中缀表达式转为后缀表达式):

(1)初始化一个堆栈。
(2)顺序读入中缀表达式,读到的字符为数字时将其输出,接着读下一个字符,遇见符号就入栈;符号入栈过程中,要比较准备 入栈的符号和栈顶操作符的优先级。
(3)当遇见括号形成匹配关系的时候,就把在括号匹配内的符号出栈。

5.后缀表达式求值算法(逆波兰表达式求值)

(1)初始化一个堆栈;
(2)定义两个操作数opt和opt2;
(3)如果对后缀表达式进行遍历,当遇到操作符的时候就从栈顶中取出两个元素以操作符进行四则运算,并 将运算后得出的结果进行入栈。否则就是遇到操作数字,就直接入栈。
(4)最后返回在栈中的结果stack.pop()。

6.代码展示:

@Test
    public void test(){
        char[] chars = reversePolish("(2 + 1) * 3");
        System.out.println();
        int result = evalRPN(chars);
        System.out.print(result);
    }

    //用来做操作符入栈的栈
    private static Stack<Character> stack = new Stack<>();

    private static LinkedList<Character> list = new LinkedList<>();


    /**
     * 根据后缀表达式计算结果
     * @param tokens
     * @return
     */
    public static int evalRPN(char[] tokens) {
        Stack<Integer> stack = new Stack();
        int opt1 = 0;   //定义两个操作数
        int opt2 = 0;
        for(char token:tokens){
            if(isOperator(token)){      //如果是运算符
                opt1 = stack.pop();
                opt2 = stack.pop();

                //进行运算
                switch (token){
                    case '+':
                        opt2 += opt1;
                        break;
                    case '-':
                        opt2 -= opt1;
                        break;
                    case '*':
                        opt2 *= opt1;
                        break;
                    case '/':
                        opt2 /= opt1;
                        break;
                }
                //将运算结果入栈
                stack.push(opt2);
            }else{          //证明是操作数,直接入栈
                if(token!=' '){
                    stack.push(Integer.parseInt(String.valueOf(token)));
                }
            }
        }
        return stack.pop();
    }

    /**
     * 传入一个四则运算式(中缀表达式)
     * @param expression
     * @return  返回一个队列 后面装有后缀表达式
     */
    public static char[] reversePolish(String expression){
        if(expression == null){
            return null;
        }
        char[] chars = expression.toCharArray();
        for(char s : chars) {
            if (isOperator(s)) {
                if (stack.isEmpty()) {  //如果栈之前是空的,直接把操作符入栈
                    stack.push(s);
                } else {
                    //如果读入的操作符为非")"且优先级比栈顶元素的优先级高或一样,则将操作符压入栈
                    if (priority(stack.peek()) <= priority(s) && s != ')') {
                        stack.push(s);

                    } else if (priority(stack.peek()) > priority(s) && s != ')') {
                        while (stack.size() != 0 && stack.peek()!= '(') {
                            char operator = stack.pop();
                            list.offer(operator);
                        }
                        stack.push(s);

                    } else if (s == ')') {
                        while (stack.peek() != '(') {
                            char operator = stack.pop();
                            list.offer(operator);
                        }
                        //while循环执行结束后,还要弹出"("
                        stack.pop();
                    }
                }

            } else {    //不是操作符的话直接加到队列中
                list.offer(s);
            }
        }

        //把剩下的操作符也添加到队列中
        while (!stack.isEmpty()) {
            char operator = stack.pop();
            list.offer(operator);
        }

        char[] c = new char[list.size()];
        int i = 0;
        while(!list.isEmpty()){
            c[i] = list.poll();
            System.out.print(c[i]);
            i++;
        }
        return c;
    }

    /**
     * 判断是不是操作符
     * @param oper
     * @return
     */
    private static boolean isOperator(char oper){
        if (oper == ('+') || oper==('-') || oper==('*')
                || oper==('/') || oper==('(') || oper== (')')) {
            return true;
        }
        return false;
    }

    /**
     * 计算操作符的优先级
     * @param s
     * @return
     */
    private static int priority(char s){
        switch (s) {
            case '+':
                return 1;
            case '-':
                return 1;
            case '*':
                return 2;
            case ('/'):
                return 2;
            case '(':
                return 3;
            case ')':
                return 3;
            default :
                return 0;
        }
    }

7.栈的应用:

(1) Stack的源码

(2) Android分层显示架构(如Activity、渲染等)

我们就说这么多,主要是理解栈的后进先出的原理,如果能够使用栈来实现逆波兰表达式,也就基本掌握了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容