• 数据结构---栈及四则运算实现


    假设我们要求输入类似这样一个表达式:9+(3-1)*3+10/2,输出结果。我们知道先括号,再乘除,最后加减,中学时候使用的科学计算器,是允许输入这样的表达式计算结果的,那么计算机怎么知道这个串里面先算括号再算乘除呢?我们先来介绍下栈这种数据结构,再来解决这个问题。

    前面已经说过数组的连表,现在来说另外一种线性表的数据结构---栈。

    举个比较形象的例子,洗盘子的时候,是不是一个一个往上面堆着放,拿的时候也从上面一个一个的拿,最先放的在最下面,最后放的在最上面,拿的时候第一个拿到。这就是典型的栈结构。先进后出First In Last Out(FILO).

    怎么来实现一个栈结构呢,栈也是一种线性表,前面也有提到两种很基础的线性表结构的数据结构数组和链表。栈其实就是第一个特殊的链表或者数组。可以基于数组或者链表来实现,成为数组栈或者链栈,与之具有数组和链表相关特点。

    栈的特殊点在于先进去的元素放在栈低,后进的在栈顶。向栈中插入一个元素叫入栈、进栈、压栈都行,插入的数据会被放在栈顶。从栈中取出一个元素叫出栈、退栈都行,取出之后,原本栈顶的这个元素就会被删掉,让它下面的那个元素成为新的栈顶元素。

    数组栈一般栈低是索引开始的元素,压栈就往索引增长方向走;链栈一般栈低是头结点,栈顶是尾结点。

    既然都是用数组或链表来实现,为什么还单独拎出来一个数据结构呢。数组和链表暴露了太多了的操作。就会更容易出错。针对性的封装出来的栈这种结构,在某些场景会更加适合。想象一下我们浏览器的的前进后退,是不是就很像两个栈的数据在互相交换操作,一个前进栈,一个后退栈。点后退,把后退栈的栈顶弹出,放进前进栈的栈顶;再点前进,是不是就是压进前进栈顶的后退栈的栈顶元素。就这样互相交替着。

    想象一个程序的调用流程是不是也是一个栈结构。最后调用的方法最先执行。

    Java里面的Stack也是基于数组实现的,它继承了Vector。我们用数组实现一个简单栈的基本操作:

    package com.nijunyang.algorithm.stack;
    
    /**
     * Description:
     * Created by nijunyang on 2020/4/1 23:48
     */
    public class MyStack<E> {
    
        private static final int DEFAULT_SIZE = 10;
    
        private Object[] elements;
    
        private int size;
    
        public MyStack() {
            this(DEFAULT_SIZE);
        }
    
        public MyStack(int capacity) {
            this.elements = new Object[capacity];
        }
    
        /**
         * 入栈
         * @param e
         */
        public void push(E e) {
            //弹性伸缩,扩容/收缩释放内存空间
            if (size >= elements.length) {
                resize(size * 2);
            } else if (size > 0 && size < elements.length / 2) {
                resize(elements.length / 2);
            }
            elements[size++] = e;
        }
    
        /**
         * 出栈
         */
        public E pop() {
            if (isEmpty()) {
                return null;
            }
            E e = (E) elements[--size];   //size是5,那么最后一个元素就是4也就是--size
            elements[size] = null;        //现在size已经是4了,弹出就是4这个元素的位置置为空
            return e;
        }
    
        public boolean isEmpty() {
            return size == 0;
        }
    
        public int size() {
            return size;
        }
    
        /**
         * 扩容/收缩
         */
        private void resize(int newCapacity) {
            Object[] temp =  new Object[newCapacity];
            for(int i = 0 ; i < size; i ++){
                temp[i] = elements[i];
            }
            elements = temp;
        }
    
        public static void main(String[] args){
            MyStack<Integer> myStack = new MyStack(5);
            myStack.push(1);
            myStack.push(2);
            myStack.push(3);
            myStack.push(4);
            myStack.push(5);
            myStack.push(6);
            System.out.println(myStack.size);
            Integer e = myStack.pop();
            System.out.println(e);
            e = myStack.pop();
            System.out.println(e);
            e = myStack.pop();
            System.out.println(e);
            e = myStack.pop();
            System.out.println(e);
            e = myStack.pop();
            System.out.println(e);
            e = myStack.pop();
            System.out.println(e);
            e = myStack.pop();
            System.out.println(e);
        }
    
    
    }

    现在用我们看看怎么用栈来解决9+(3-1)*3+10/2这个计算问题

    首先我们要怎么来处理括号和运算符号的优先级呢

    这里先说一下中缀表达式和后缀表达式,像这个表达式9+(3-1)*3+10/2就是中缀表达式,如果我们转换成9 3 1 - 3 * + 10 2 / + 这个就是后缀表达式,后缀表达式也叫逆波兰,可以可以自行百度或者google,后缀表达式就是操作符号在两个操作数的后面,而中缀表达式就是操作符号在两个操作数的中间。

    看下后缀表达式是怎么操作的,就是遇到操作符号就把前面两个数进行符号运算:

    9 3 1 - 3 * + 10 2 / + 这个表达式的操作如下:

    9 3 1 - 这个时候就把31 相减得到2 => 9 2 3 * 这个时候就把23相乘 得到6 =>

    9 6 + => 15 10 2 / =>15 5 + => 20

    大致就是这么个流程,这个过程是不是很像栈的操作,遇到数字就入栈,遇到符号就把数字前面两个数字出栈进行计算,然后将结果入栈,直到表达式结束。

    现在我们只要把中缀表达式转换成后缀表达式就可以进行计算了。看下百度的转换流程

    简单来说就是用一个栈来存放符号,然后从左到右遍历中缀表达式的数字和字符,若是数字就输出,若是符号则判断和栈顶符号的优先级,如果是括号或优先级低于栈顶元素,则依次出栈并输出,将当前符号进栈,直到最后结束。

    9+(3-1)*3+10/2

    先初始化一个栈stack,然后依次遍历我们的中缀表达式,操作逻辑如下:

    1. 9 输出 => 9
    2. + 栈空的直接进栈:stack+
    3. ( 未配对的 直接进栈:stack+ (
    4. 3 数字直接输出:9 3
    5. - 前面是( 直接进栈:stack + ( -
    6. 1 直接输出: 9 3 1
    7. )  将前面的符号弹出输出,直到匹配到第一个(为止:9 3 1 -  stack: +
    8. * 优先级高于 + 进栈: stack+ *
    9. 3 输出 9 3 1 - 3
    10. + 优先级低于栈顶的* 将栈顶弹出输出 继续判断之后栈顶是否比+优先级低(同级也弹出,直到有限比栈顶高,或者空栈为止),这里就会连续弹出 * + 然后将当前的 + 入栈:9 3 1 - 3 * +     stack: +
    11. 10 输出:9 3 1 - 3 * + 10
    12. / 优先级高于栈顶 + 直接入栈:stack+ /
    13. 2 直接输出: 9 3 1 - 3 * + 10 2
    14. 最后符号依次出输出:9 3 1 - 3 * + 10 2 / +

     

    从上述逻辑中可以看到,不管是最后的计算,还是中缀表达式转后缀表达式中都用到栈这种数据结构。

  • 相关阅读:
    jquery的ready和简写
    javasript之ajax学习笔记
    用 Drupal 创建更好的评分系统的具体步骤
    drupal的FIVESTAR投票模块说明
    drupal中时间自定义格式
    drupal首页不显示默认内容列表方法
    基于 Zen 创建一个 Drupal 7 的主题(模板) ,一份简单的Drupal模板教程
    jquery tooltip事件
    HTML5地区自转代码
    转:Jmeter常见问题 (转载) http://www.51testing.com/?uid-128005-action-viewspace-itemid-84094
  • 原文地址:https://www.cnblogs.com/nijunyang/p/12623863.html
Copyright © 2020-2023  润新知