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

假设我们要求输入类似这样一个表达式: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 - 这个时候就把3和1 相减得到2 => 9 2 3 * 这个时候就把2和3相乘 得到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 / +

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

原文地址:https://www.cnblogs.com/nijunyang/p/12623863.html

时间: 2024-10-30 23:27:44

数据结构---栈及四则运算实现的相关文章

20172333 2017-2018-2 《程序设计与数据结构》实验四则运算报告

20172333 2017-2018-2 <程序设计与数据结构>实验四则运算报告 1.结对成员 李楠20172330 担任角色(团队分工) [x] 1.前期分析流程 领航员:严域俊 操作员:李楠 原因:我对于整个四则运算过程要求进行一一确定需要编写什么样的类,而李楠则是对于我们要编写的类寻找一些方法来进行实现,以及如何方便快捷的进行编写. [x] 2.中期各自分工编写类 该阶段主要是进行各自任务分工,由于之前的讨论我们的目的较为明确分为三个步奏,第一是我进行编写一个可以随机生成不同等级的题目类

南阳OJ-2 括号配对 (数据结构-栈的应用)

括号配对问题 时间限制:3000 ms  |  内存限制:65535 KB 难度:3 描述 现在,有一行括号序列,请你检查这行括号是否配对. 输入 第一行输入一个数N(0<N<=100),表示有N组测试数据.后面的N行输入多组输入数据,每组输入数据都是一个字符串S(S的长度小于10000,且S不是空串),测试数据组数少于5组.数据保证S中只含有"[","]","(",")"四种字符 输出 每组输入数据的输出占一行,

poj 2082 Terrible Sets (数据结构 ——栈 STL)

 Terrible Sets Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 2999   Accepted: 1549 Description Let N be the set of all natural numbers {0 , 1 , 2 , . . . }, and R be the set of all real numbers. wi, hi for i = 1 . . . n are some elem

数据结构——栈——寻找下一个较大元素

题目描述 给出一个数组,向右寻找每一个元素的下一个较大的元素,没有更大的则为-1 举例 {4,6,1,3,2,5} 则求得的答案应为 {6,-1,3,5,5,-1} 题目分析 首先对于这样的题目,我们总是先想到最简单的,那么就是枚举,每次循环一个元素,不停的向右找就可以了.时间复杂度应该是n^2 但是这样肯定是不够用的. 然后我们考虑,这道题我们实际上遇到的问题是什么? 其实简单的说,这道题的意思是,在变化的数组中找到下一个较大的值. 难点在于,数组元素的变化,以及不是找最大值,而是找下一个较大

C数据结构-栈和队列,括号匹配举例

1.栈和队列是两种特殊的线性表 运算操作被限定只能在表的一端或两端插入,删除元素,故也称它们为限定的线性表结构 2.栈的基本运算 1).Stackinit(&s) 构造一个空栈 2).Stackempty(s) 判断s是否为空栈,当s为空栈时,函数返回值1 否则 0 3).Push(&s,x)  在栈s 的顶部插入元素x,简称将x入 栈 4).Pop(&s,&x) 在栈s 中删除顶元并将其值保存在x单元中返回,简称将x出栈 5)Gettop(s,&x)  读s栈中的

数据结构-栈的压入、弹出序列

题目:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个顺序是否是该栈的弹出顺序.假设压入栈的所有数字均不相等.例如序列1,2,3,4,5是某栈的压栈序列,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但是4,3,5,1,2就不可能是. 分析:首先利用图像进行形象化的理解.可以发现其压入的顺序并不是所有的一次性压入.同时要考虑栈是否为空.以及其长度.这段代码写的很痛苦,继续训练. /* 剑指offer面试题22 */ #include <iostream> #include &

利用数据结构栈求解迷宫问题

本段程序主要利用数据结构栈的先进后出特点,实现回溯求解迷宫路径问题. #include<iostream> #include<stack> using namespace std; //坐标类 struct Point { int x; int y; }; //地图类 template<int A> struct Map { int (*p)[A]; int row;//行数 int col;//列数 }; //start起始点, end终止点 template<

数据结构 栈笔记

// 数据结构 栈的实现 C语言, 只能从头部储存和销毁数据 # include <stdio.h> # include <stdlib.h> # include <malloc.h> // 线性储存部分 typedef struct NONE { int data; struct NONE * pNext; }None, * pNone; // 栈的头部和底部 typedef struct stack { pNone top; pNone bottom; }St, *

大话数据结构----栈

栈的定义: 栈(stack)是限定尽在表尾进行插入和删除操作的线性表. 从定义中可以看出,栈也是线性表,是一个特殊的线性表,之前说线性的表的时候,线性表可以在任意位置进行插入插入,而栈比线性表特殊的地方的就是不能随意的插入和删除了,栈的插入和删除只能在规定的同一端进行,而被允许插入和删除的一端称为栈顶,另一端称为栈底. 从栈的特性中我们能发现:1).栈是一种后进先出的线性表,因为栈只有一个出入口,每次出栈是最后进的那个先出去,后一个才能出去,就像我们出电梯一样,里面的人被阻住,只有外面的人出去,