(八)数据结构-栈

命运对每个人都是一样的,不一样的是各自的努力和付出不同,付出的越多,努力的越多,得到的回报也越多,在你累的时候请看一下身边比你成功却还比你更努力的人,这样,你就会更有动力。

导读:本篇文章讲解 (八)数据结构-栈,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

1.栈的一个实际需求

请输入一个表达式,计算式:[7*2*2-5+1-5+3-3]

请问:计算机底层是如何运算得到结果的?注意不是简单的把算式列出运算,因为我们看这个算式 7*2*2-5,但是计算机怎么理解这个算式的(对计算机而言,它接收到的就是一个字符串),我们讨论的是这个问题 -> 栈。

(八)数据结构-栈

2.栈的介绍

(1)栈的英文为(stack);

(2)栈是一个先入后出(FILO-First In Last Out)的有序列表;

(3)栈(stack)是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的一端,为变化的一端,称为栈顶(Top),另一端为固定的一端,称为栈底(Bottom);

(4)根据栈的定义可知,最先放入栈中的元素在栈底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除。

出站和入站示意图。

(八)数据结构-栈

(八)数据结构-栈

2.1栈的应用场景

(1)子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,知道子程序执行完后再将地址取出,以回到原来的程序中。

(2)处理递归调用:和子程序的调用类似,只是除了存储下一个指令的地址外,也将参数、区域变量等数据存入到堆栈中。

(3)表达式的转换[中缀表达式转后缀表达式]与求值。

(4)二叉树的遍历。

(5)图形的深度优先(depth-first)搜索法。

2.2栈的快速入门

用数组模拟栈的使用,由于栈是一种有序列表,当然可以使用数组的结构来存储栈的数据内容,下面我们就用数组模拟栈的出栈,入栈等操作。

实现栈的思路分析:

(1)使用数组模拟栈;

(2)定义一个 top 来表示栈顶,初始化为 -1;

(3)入栈的操作,当有数据加入到栈时,

top++;

stack[top]=data;

(4)出栈的操作,

int value=stack[top];

top–;

return value;

代码实现:

/**
 * 数组模拟栈
 */
public class ArrayStackDemo {
    public static void main(String[] args) {
        // 测试
        // 创建一个对象
        ArrayStack arrayStack = new ArrayStack(4);

        String key = "";
        boolean loop = true;
        Scanner scanner = new Scanner(System.in);

        while (loop){
            System.out.println("show:表示显示栈");
            System.out.println("exit:表示退出程序");
            System.out.println("push:表示添加数据到栈(入站)");
            System.out.println("pop:表示从栈中取出数据(出栈)");
            System.out.println("请输入您的选择");
            key = scanner.next();
            switch (key){
                case "show":
                    arrayStack.list();
                    break;
                case "push":
                    System.out.println("请输入一个数:");
                    int data = scanner.nextInt();
                    arrayStack.push(data);
                    break;
                case "pop":
                    Integer pop = arrayStack.pop();
                    System.out.printf("出栈的数据是%d\n", pop);
                    break;
                case "exit":
                    scanner.close();
                    System.out.println("退出程序");
                    loop = false;
                    break;
                default:
                    break;
            }
        }
    }
}

// 定义一个ArrayStack类,表示栈
class ArrayStack {
    // 栈的大小
    private int maxSize;
    // 数组,数组模拟栈,数据就放在该数组
    private int[] stack;
    // 栈顶
    private int top = -1;

    public ArrayStack(int maxSize) {
        this.maxSize = maxSize;
        this.stack = new int[maxSize];
    }

    // 判断栈满
    public boolean isFull() {
        return top == maxSize - 1;
    }

    // 判断栈空
    public boolean isEmpty() {
        return top == -1;
    }

    // 入站操作
    public void push(int value) {
        // 判断栈是否满
        if (isFull()) {
            System.out.println("栈满");
            return;
        }

        top++;
        stack[top] = value;
    }

    // 出栈操作
    public Integer pop() {
        // 判断栈是否为空
        if (isEmpty()) {
            System.out.println("栈空,没有数据");
            return null;
        }

        int data = this.stack[top];
        top--;
        return data;
    }

    // 显示栈情况-遍历栈,遍历时需要从栈顶开始显示数据
    public void list() {
        if (isEmpty()) {
            System.out.println("栈空,没有数据");
            return;
        }
        for (int i = top; i >= 0; i--){
            System.out.printf("stack[%d]=%d\n", i, stack[i]);
        }
    }
}

3.栈实现综合计算器

需求:使用栈来实现综合计算器。

使用栈完成计算一个表达式的结果,假如表达式为 7*2*2-5+1-5+3-4

使用栈完成表达式的计算思路:

(1)通过一个 index 值(索引),来遍历我们的表达式;

(2)如果我们发现是一个数字,就直接入数栈;

(3)如果发现扫描到的是一个符号,就分如下情况;

3.1 如果发现当前的符号栈为空,就直接入栈;

3.2 如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符,就需要从数栈中 pop 出两个数,在从符号栈中 pop出一个符号,进行运算,将得到的结果,入数栈,然后将当前的操作符入符号栈,如果当前的操作符的优先级大于栈中的操作符,就直接入符号栈。

(4)当表达式扫描完毕,就顺序的从数栈和符号栈中 pop 出相应的数和符号,并运行。

(5)最后在数栈只有一个数字,就是表达式的结果。

(八)数据结构-栈

代码示例:

public class Calculator {
    public static void main(String[] args) {
        // 表达式运算
        String expression = "3+2*6-2";
        // 创建两个栈,数栈,符号栈
        ArrayStack2 dataStack = new ArrayStack2(10);
        ArrayStack2 operStack = new ArrayStack2(5);
        // 用于扫描
        int index = 0;
        int num1 = 0;
        int num2 = 0;
        int oper = 0;
        int res = 0;
        // 将每次扫描得到char保存到ch
        char ch = ' ';

        while (true) {
            // 先得到expression的每一个字符
            ch = expression.substring(index, index + 1).charAt(0);
            // 判断优先级
            if (operStack.isOper(ch)) {
                // 如果是运算符
                // 判断当前栈是否为空
                if (!operStack.isEmpty()) {
                    // 如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小宇或者等于栈中的操作符,就需要从数栈中 pop 出两个数,在从符号栈中 pop出一个符号,进行运算,将得到结果,入数栈,
                    // 然后将当前的操作符入符号栈,如果当前的操作符的优先级大于栈中的操作符,就直接入符号栈
                    if (operStack.priority(ch) <= operStack.priority(operStack.peek())) {
                        num1 = dataStack.pop();
                        num2 = dataStack.pop();
                        oper = operStack.pop();
                        res = dataStack.cal(num1, num2, oper);
                        // 把运算结果入栈
                        dataStack.push(res);
                        // 然后将当前的操作符入符号栈
                        operStack.push(ch);
                    } else {
                        // 如果当前的操作符的优先级大于栈中的操作符,就直接入符号栈
                        operStack.push(ch);
                    }
                } else {
                    //如果为空,直接入栈
                    operStack.push(ch);
                }
            } else {
                // 如果是数,则直接入数栈
                dataStack.push(ch - 48);
            }

            // 让 index + 1,判断是否扫描到 expression 最后
            index++;
            if (index >= expression.length()) {
                break;
            }
        }

        while (true) {
            // 如果符号栈为空,则计算到最后的结果,数栈中只有一个数字【结果】
            if (operStack.isEmpty()) {
                break;
            }
            num1 = dataStack.pop();
            num2 = dataStack.pop();
            oper = operStack.pop();
            res = dataStack.cal(num1, num2, oper);
            // 把运算结果入栈
            dataStack.push(res);
        }

        System.out.printf("表达式%s == %d", expression, dataStack.pop());
    }
}

// 先创建一个栈
class ArrayStack2 {
    // 栈的大小
    private int maxSize;
    // 数组,数组模拟栈,数据就放在该数组
    private int[] stack;
    // 栈顶
    private int top = -1;

    public ArrayStack2(int maxSize) {
        this.maxSize = maxSize;
        this.stack = new int[maxSize];
    }

    // 判断栈满
    public boolean isFull() {
        return top == maxSize - 1;
    }

    // 判断栈空
    public boolean isEmpty() {
        return top == -1;
    }

    // 入站操作
    public void push(int value) {
        // 判断栈是否满
        if (isFull()) {
            System.out.println("栈满");
            return;
        }

        top++;
        stack[top] = value;
    }

    // 出栈操作
    public Integer pop() {
        // 判断栈是否为空
        if (isEmpty()) {
            System.out.println("栈空,没有数据");
            return null;
        }

        int data = this.stack[top];
        top--;
        return data;
    }

    // 显示栈情况-遍历栈,遍历时需要从栈顶开始显示数据
    public void list() {
        if (isEmpty()) {
            System.out.println("栈空,没有数据");
            return;
        }
        for (int i = top; i >= 0; i--) {
            System.out.printf("stack[%d]=%d\n", i, stack[i]);
        }
    }

    // 返回栈顶值
    public int peek() {
        return stack[top];
    }

    // 返回运算符的优先级,优先级使用数字表示,数字越大则优先级越高
    // 假定目前的表达式只有 + - + /
    public int priority(int oper) {
        if (oper == '*' || oper == '/') {
            return 1;
        } else if (oper == '+' || oper == '-') {
            return 0;
        } else {
            return -1;
        }
    }

    // 判断是不是一个运算符
    public boolean isOper(char val) {
        return val == '+' || val == '-' || val == '*' || val == '/';
    }

    // 计算方法
    public int cal(int num1, int num2, int oper) {
        // 用于存放计算的结果
        int res = 0;
        switch (oper) {
            case '+':
                res = num1 + num2;
                break;
            case '-':
                res = num2 - num1;
                break;
            case '*':
                res = num1 * num2;
                break;
            case '/':
                res = num2 / num1;
                break;
            default:
                break;
        }
        return res;
    }
}

4.前缀、中缀、后缀表达式(逆波兰表达式)

4.1前缀表达式

(1)前缀表达式又称波兰式,前缀表达式的运算符位于操作数之前;

(2)举例说明:(3+4)*5-6 对应的前缀表达式就是 – * + 3 4 5 6

前缀表达式的计算机求值

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

例如:(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,因此得出最终结果。

4.2中缀表达式

(1)中缀表达式就是常见的运算表达式,如 (3+4)*5-6;

(2)中缀表达式的求值是我们人最熟悉的,但是对计算机来说却不好操作,因此,在计算结果时,往往会将中缀表达式转成其它表达式来操作(一般转成后缀表达式)。

4.3后缀表达式

(1)后缀表达式又称逆波兰表达式,与前缀表达式相似,只是运算符位于操作数之后;

(2)举例说明:(3+4)*5-6 对应的后缀表达式就是 3 4 + 5 * 6 –

(3)再比如:

正常的表达式

逆波兰表达式

a+b

a b +

a+(b-c)

a b c – +

a+(b-c)*d

a b c – d * +

a+d*(b-c)

a d b c – * +

a=1+3

a 1 3 + =

后缀表达式的计算机求值

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

例如:(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 入栈;

(6)最后是 – 运算符,计算出 35-6 的值,即 29,因此得出最终结果。

4.4逆波兰计算器

完成一个逆波兰计算器,要求完成如下任务:

(1)输入一个逆波兰表达式(后缀表达式),使用栈(stack),计算其结果;

(2)支持小括号和多位数整数,因为这里主要讲的是数据结构,因此计算器进行简化,只支持对整数的计算。

代码示例:

public class PolandNotation {
    public static void main(String[] args) {
        // 先定义一个逆波兰表达式
        // (3+4)x5-6
        // 为了方便,逆波兰表达式的数字和符号使用空格隔开
        String suffixExpression = "3 4 + 5 * 6 - ";

        // 思路
        // 1. 现将"3 4 + 5 x 6 - " => 放到 ArrayList 中
        // 2. 将ArrayList 传递给一个方法,配合栈完成计算

        List<String> rpnList = getListString(suffixExpression);
        System.out.println(rpnList);

        int calculate = calculate(rpnList);
        System.out.println("计算的结果="  + calculate);
    }

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

        return list;
    }

    // 完成对逆波兰表达式的运算
    /**
     * (1)从左至右扫描,将3和4压入堆栈;
     * (2)遇到 + 运算符,因此弹出 4 和 3(4 为栈顶元素,3为次顶元素),计算出 3+4 的值,得7,再将 7 入栈;
     * (3)将 5 入栈;
     * (4)接下来是 * 运算符,因此弹出 5和7,计算出 7*5=35,将 35 入栈;
     * (5)将6 入栈;
     * (6)最后是 - 运算符,计算出 35-6 的值,即 29,因此得出最终结果。
     */
    public static int calculate(List<String> list){
        // 创建一个栈,只需要一个栈即可
        Stack<String> stack = new Stack<>();

        // 遍历 list
        for (String item : list) {
            // 这里使用正则表达式来取出数
            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 + "");
            }
        }
        return Integer.parseInt(stack.peek());
    }
}

4.5中缀表达式转换为后缀表达式

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

具体步骤如下:

(1)初始化两个栈:运算符栈 s1 和存储中间结果的栈 s2;

(2)从左至右扫描中缀表达式;

(3)遇到操作数时,将其压 s2;

(4)遇到运算符时,比较其与 s1 栈顶运算符的优先级:

  1. 如果 s1 为空,或栈顶运算符为左括号 “(“,则直接将此运算符入栈;

  1. 否则,若优先级比栈顶运算符的高,也将运算符压入 s1;

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

(5)遇到括号时:

  1. 如果是左括号 “(“,则直接压入 s1;

  1. 如果是右括号 “)”,则依次弹出 s1 栈顶的运算符,并压入 s2,直到遇到左括号为止,此时将这一对括号丢弃;

(6)重复步骤 2 至 5,直到表达式的最右边;

(7)将 s1 中剩余的运算符依次弹出并压入 s2;

(8)依次弹出 s2 中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式。

举例说明:

将中缀表达式 “1+((2+3)x4)-5” 转换成后缀表达式的过程如下

扫描到的元素

s2(栈底->栈顶)

s1(栈底->栈顶)

说明

1

1

数字,直接入站

+

1

+

s1 为空,运算符直接入栈

(

1

+ (

左括号,直接入栈

(

1

+ ( (

左括号,直接入栈

2

1 2

+ ( (

数字

+

1 2

+ ( ( +

s1栈顶为左括号,运算符直接入栈

3

1 2 3

+ ( ( +

数字

)

1 2 3 +

+ (

右括号,弹出运算符直到遇到左括号

x

1 2 3 +

+ ( x

s1栈顶为左括号,运算符直接入栈

4

1 2 3 + 4

+ ( x

数字

)

1 2 3 + 4 x

+

右括号,弹出运算符直到遇到左括号

1 2 3 + 4 x +

  • – 与 + 优先级相同,因此弹出 +,在压入 –

5

1 2 3 + 5 x + 5

数字

到达最右端

1 2 3 + 5 x + 5 –

s1 中剩余的运算符

因此结果为: “1 2 3 + 4 x + 5 -“

代码示例如下:

public class PolandNotation2 {
    public static void main(String[] args) {
        // 完成将中缀表达式转成后缀表达式的功能
        // 说明
        // 1. 1+((2+3)x4)-5  => 1 2 3 + 4 x + 5 -
        // 2. 因为直接对 str 进行操作,不方便,因此先将 "1+((2+3)x4)-5"  => 中缀的表达式对应的List
        // 即 "1+((2+3)x4)-5"  =>  ArrayList[1,+,(,(,2,+,3,),x,4),-,5]
        // 3. 将得到的中缀表达式对应的 List -> 后缀表达式对应的List

        String expression = "10+((2+3)x4)-5";

        List<String> list = toInfixExpressionList(expression);
        System.out.println("----------中缀表达式LIst--------");
        System.out.println(list);

        List<String> list1 = parseSuffixExpressionList(list);
        System.out.println("----------后缀表达式LIst--------");
        System.out.println(list1);

        System.out.printf("expression等于 %d", calculate(list1));


    }

    // 将得到的中缀表达式对应的 List -> 后缀表达式对应的List
    public static List<String> parseSuffixExpressionList(List<String> list) {
        // 定义两个栈
        // 符号栈
        Stack<String> s1 = new Stack<>();
        // 存储中间结果的栈 说明:因为 s2这个栈,在整个转换过程中,没有 pop操作,而且后面我们还需要逆序输出,
        // 因此比较麻烦,这里我们就不用 Stack<String>,直接使用 List<String> s2
        List<String> s2 = new ArrayList<>();

        // 遍历list
        for (String item : list) {
            // 如果是一个数,加入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 栈顶运算符的优先级,将s1栈顶的运算符弹出并加入到 s2 中
                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
    public static List<String> toInfixExpressionList(String str) {
        // 定义一个List
        List<String> list = new ArrayList<>();
        // 辅助指针,用于遍历 中缀表达式字符串str
        int index = 0;
        // 多位数的拼接
        String s = "";
        char c;

        do {
            // 如果 c 是一个非数字,需要加入到 list 中
            if ((c = str.charAt(index)) < 48 || (c = str.charAt(index)) > 57) {
                list.add(c + "");
                // 指针后移
                index++;
            } else {
                // 如果是一个数,需要考虑多位数的问题
                // 置空
                s = "";
                while (index < str.length() && (c = str.charAt(index)) >= 48 && (c = str.charAt(index)) <= 57) {
                    // 拼接
                    s += c;
                    index++;
                }
                list.add(s);
            }
        } while (index < str.length());

        return list;
    }

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

        return list;
    }

    // 完成对逆波兰表达式的运算

    /**
     * (1)从左至右扫描,将3和4压入堆栈;
     * (2)遇到 + 运算符,因此弹出 4 和 3(4 为栈顶元素,3为次顶元素),计算出 3+4 的值,得7,再将 7 入栈;
     * (3)将 5 入栈;
     * (4)接下来是 x 运算符,因此弹出 5和7,计算出 7x5=35,将 35 入栈;
     * (5)将6 入栈;
     * (6)最后是 - 运算符,计算出 35-6 的值,即 29,因此得出最终结果。
     */
    public static int calculate(List<String> list) {
        // 创建一个栈,只需要一个栈即可
        Stack<String> stack = new Stack<>();

        // 遍历 list
        for (String item : list) {
            // 这里使用正则表达式来取出数
            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("x")) {
                    res = num1 * num2;
                } else if (item.equals("-")) {
                    res = num1 / num2;
                } else {
                    throw new RuntimeException("运算符符号异常");
                }

                // 把 res 入栈
                stack.push(res + "");

            }
        }

        return Integer.parseInt(stack.peek());


    }

}

// 编写一个类 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;
        switch (operation) {
            case "+":
                result = ADD;
                break;
            case "-":
                result = SUB;
                break;
            case "x":
                result = MUL;
                break;
            case "/":
                result = DIV;
                break;
            default:
                System.out.println("不存在的运算符");
                break;
        }

        return result;
    }
}

5. 逆波兰计算器完整版

public class ReversePolishMultiCalc {

    /**
     * 匹配 + - * / ( ) 运算符
     */
    static final String SYMBOL = "\\+|-|\\*|/|\\(|\\)";

    static final String LEFT = "(";
    static final String RIGHT = ")";
    static final String ADD = "+";
    static final String MINUS= "-";
    static final String TIMES = "*";
    static final String DIVISION = "/";

    /**
     * 加減 + -
     */
    static final int LEVEL_01 = 1;
    /**
     * 乘除 * /
     */
    static final int LEVEL_02 = 2;

    /**
     * 括号
     */
    static final int LEVEL_HIGH = Integer.MAX_VALUE;


    static Stack<String> stack = new Stack< String>();
    static List<String> data = Collections.synchronizedList(new ArrayList<String>());

    /**
     * 去除所有空白符
     * @param s
     * @return
     */
    public static String replaceAllBlank(String s ){
        // \\s+ 匹配任何空白字符,包括空格、制表符、换页符等等, 等价于[ \f\n\r\t\v]
        return s.replaceAll("\\s+","");
    }

    /**
     * 判断是不是数字 int double long float
     * @param s
     * @return
     */
    public static boolean isNumber(String s){
        Pattern pattern = Pattern.compile("^[-\\+]?[.\\d]*$");
        return pattern.matcher(s).matches();
    }

    /**
     * 判断是不是运算符
     * @param s
     * @return
     */
    public static boolean isSymbol(String s){
        return s.matches(SYMBOL);
    }

    /**
     * 匹配运算等级
     * @param s
     * @return
     */
    public static int calcLevel(String s){
        if("+".equals(s) || "-".equals(s)){
            return LEVEL_01;
        } else if("*".equals(s) || "/".equals(s)){
            return LEVEL_02;
        }
        return LEVEL_HIGH;
    }

    /**
     * 匹配
     * @param s
     * @throws Exception
     */
    public static List<String> doMatch (String s) throws Exception{
        if(s == null || "".equals(s.trim())) throw new RuntimeException("data is empty");
        if(!isNumber(s.charAt(0)+"")) throw new RuntimeException("data illeagle,start not with a number");

        s = replaceAllBlank(s);

        String each;
        int start = 0;

        for (int i = 0; i < s.length(); i++) {
            if(isSymbol(s.charAt(i)+"")){
                each = s.charAt(i)+"";
                //栈为空,(操作符,或者 操作符优先级大于栈顶优先级 && 操作符优先级不是( )的优先级 及是 ) 不能直接入栈
                if(stack.isEmpty() || LEFT.equals(each)
                        || ((calcLevel(each) > calcLevel(stack.peek())) && calcLevel(each) < LEVEL_HIGH)){
                    stack.push(each);
                }else if( !stack.isEmpty() && calcLevel(each) <= calcLevel(stack.peek())){
                    //栈非空,操作符优先级小于等于栈顶优先级时出栈入列,直到栈为空,或者遇到了(,最后操作符入栈
                    while (!stack.isEmpty() && calcLevel(each) <= calcLevel(stack.peek()) ){
                        if(calcLevel(stack.peek()) == LEVEL_HIGH){
                            break;
                        }
                        data.add(stack.pop());
                    }
                    stack.push(each);
                }else if(RIGHT.equals(each)){
                    // ) 操作符,依次出栈入列直到空栈或者遇到了第一个)操作符,此时)出栈
                    while (!stack.isEmpty() && LEVEL_HIGH >= calcLevel(stack.peek())){
                        if(LEVEL_HIGH == calcLevel(stack.peek())){
                            stack.pop();
                            break;
                        }
                        data.add(stack.pop());
                    }
                }
                start = i ;    //前一个运算符的位置
            }else if( i == s.length()-1 || isSymbol(s.charAt(i+1)+"") ){
                each = start == 0 ? s.substring(start,i+1) : s.substring(start+1,i+1);
                if(isNumber(each)) {
                    data.add(each);
                    continue;
                }
                throw new RuntimeException("data not match number");
            }
        }
        //如果栈里还有元素,此时元素需要依次出栈入列,可以想象栈里剩下栈顶为/,栈底为+,应该依次出栈入列,可以直接翻转整个stack 添加到队列
        Collections.reverse(stack);
        data.addAll(new ArrayList<String>(stack));

        System.out.println(data);
        return data;
    }

    /**
     * 算出结果
     * @param list
     * @return
     */
    public static Double doCalc(List<String> list){
        Double d = 0d;
        if(list == null || list.isEmpty()){
            return null;
        }
        if (list.size() == 1){
            System.out.println(list);
            d = Double.valueOf(list.get(0));
            return d;
        }
        ArrayList<String> list1 = new ArrayList<String>();
        for (int i = 0; i < list.size(); i++) {
            list1.add(list.get(i));
            if(isSymbol(list.get(i))){
                Double d1 = doTheMath(list.get(i - 2), list.get(i - 1), list.get(i));
                list1.remove(i);
                list1.remove(i-1);
                list1.set(i-2,d1+"");
                list1.addAll(list.subList(i+1,list.size()));
                break;
            }
        }
        doCalc(list1);
        return d;
    }

    /**
     * 运算
     * @param s1
     * @param s2
     * @param symbol
     * @return
     */
    public static Double doTheMath(String s1,String s2,String symbol){
        Double result ;
        switch (symbol){
            case ADD : result = Double.valueOf(s1) + Double.valueOf(s2); break;
            case MINUS : result = Double.valueOf(s1) - Double.valueOf(s2); break;
            case TIMES : result = Double.valueOf(s1) * Double.valueOf(s2); break;
            case DIVISION : result = Double.valueOf(s1) / Double.valueOf(s2); break;
            default : result = null;
        }
        return result;

    }

    public static void main(String[] args) {
        //String math = "9+(3-1)*3+10/2";
        String math = "12.8 + (2 - 3.55)*4+10/5.0";
        try {
            doCalc(doMatch(math));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/142577.html

(0)

相关推荐

发表回复

登录后才能评论