【力扣刷题】数组实现栈、后缀表达式(逆波兰表达式)求值、中缀表达式转换为后缀表达式(无括号&&有括号)

简介: 【力扣刷题】数组实现栈、后缀表达式(逆波兰表达式)求值、中缀表达式转换为后缀表达式(无括号&&有括号)

一、数组实现栈

1.1 题目描述

给定一个栈的接口,实现该接口中的方法,接口中的方法包括向栈顶添加元素、从栈顶弹出元素、返回栈顶元素,不弹出、判断栈是否为空、判断栈是否已满,要求使用数组实现。

  • 要实现的栈的接口:
public interface Stack<E> {
    /**
     * 向栈顶添加元素
     * @param value -带压入值
     * @return  -成功返回true,失败返回false
     */
    boolean push(E value);
    /**
     * 从栈顶弹出元素
     * @return -栈非空返回栈顶元素,栈为空返回null
     */
    E pop();
    /**
     * 返回栈顶元素,不弹出
     * @return -栈非空返回栈顶元素,栈为空返回null
     */
    E peak();
    /**
     * 判断栈是否为空
     * @return -空返回true,非空返回false
     */
    boolean isEmpty();
    /**
     * 判断栈是否已满
     * @return -满返回true,不满返回false
     */
    boolean isFull();
}

1.2 思路分析

用数组来实现栈的方法相对比较简单,首先定义一个数组,用来模拟栈的功能,定义一个整型变量作为栈顶指针(int top),为这个数组栈定义一个带形参的构造方法,参数表示数组的默认长度(int capacity),即栈的容量。

可以将栈顶指针表示的索引值与数组长度相等时作为判定栈是否满的标志,即当top == array.length()时表示栈已满,当top == 0时表示栈为空。

压栈操作就是将要添加的值添加到数组索引为top的位置处,即向栈顶添加元素,然后top指向top+1索引处,当栈为满时,返回false表示添加失败。

出栈操作先判断栈是否为空,若栈为空,则直接返回false表示没有元素,否则拿到此时栈顶的元素,注意top表示的是下一个栈顶元素存放的位置,所以此时的栈顶元素的索引是top-1,返回数组下标为top-1的元素的值,然后top–表示失去一个元素

拿到栈顶元素且不弹出与出栈类似,不同的是,拿到栈顶元素且不弹出不需要将top自减,只需返回数组下标为top-1的元素的值即可。

1.3 代码演示

public class ArrayStack<E> implements Stack<E>,Iterable<E> {
    private E[] array;
    private int top;    //栈顶指针
    @SuppressWarnings("all")
    public ArrayStack(int capacity) {
        this.array = (E[]) new Object[capacity];
    }
    @Override
    public boolean push(E value) {
        if (isFull()) {
            return false;
        }
        array[top] = value;
        top++;
        return true;
    }
    @Override
    public E pop() {
        if (isEmpty()) {
            return null;
        }
        //找到栈顶元素
        E value = array[top - 1];
        top--;
        return value;
    }
    @Override
    public E peak() {
        if (isEmpty()) {
            return null;
        }
        //找到栈顶元素
        E value = array[top - 1];
        return value;
    }
    @Override
    public boolean isEmpty() {
        return top == 0;
    }
    @Override
    public boolean isFull() {
        return top == array.length;
    }
    @Override
    public Iterator<E> iterator() {
        return new Iterator<E>() {
            int p = top;
            @Override
            public boolean hasNext() {
                return p > 0;
            }
            @Override
            public E next() {
                E value = array[--p];
                return value;
            }
        };
    }
}

二、 后缀表达式(逆波兰表达式)求值

2.1 题目描述

给定一个字符串数组,这串数组是后缀表达式形式(这种表示方式把运算符写在运算对象的后面,例如,把a+b写成ab+,所以也称为后缀式。这种表示法的优点是根据运算对象和算符的出现次序进行计算,不需要使用括号,也便于用械实现求值。),要求通过代码将这串表达式的值计算出来,不需要考虑表达式的正确性,默认表达式一定有值。

2.2 思路分析

利用栈的特性对这个字符串数组进行遍历,通过对遍历到的每个元素进行判断,当遍历到的元素不是运算符时,表示这个元素是字符串类型的数字,将它转换为整型的数字并添加到栈中,当遍历到的元素是运算符时,则从栈顶弹出两个元素,并进行该运算符的计算,将计算得到的结果再次添加到栈中,当整个数组遍历完时,此时栈顶的元素就是最终的结果,直接返回栈顶元素。

2.3 代码演示

public int evalRPN(String[] tokens) {
        LinkedList<Integer> stack = new LinkedList<>();
        for (String token : tokens) {
            switch (token) {
                case "+" : {
                    Integer b = stack.pop();
                    Integer a = stack.pop();
                    stack.push(a + b);
                    break;
                }
                case "-" : {
                    Integer b = stack.pop();
                    Integer a = stack.pop();
                    stack.push(a - b);
                    break;
                }
                case "*" : {
                    Integer b = stack.pop();
                    Integer a = stack.pop();
                    stack.push(a * b);
                    break;
                }
                case "/" : {
                    Integer b = stack.pop();
                    Integer a = stack.pop();
                    stack.push(a / b);
                    break;
                }
                default: { //数字
                    stack.push(Integer.parseInt(token));
                    break;
                }
            }
        }
        return stack.pop();
    }

三、中缀表达式转换为后缀表达式(无括号)

3.1 题目描述

给定一段字符串,该字符串是一段标准的中缀表达式(如a + b - c、a * b + d、a + b * c - d等),要求将该式转换为后缀表达式。

3.2 思路分析

利用栈的特性,在遍历字符串时每次得到一个字符,设置一个优先级判断,若这个字符是数字的话,直接将它拼接在将要返回的新字符串上,若这个字符是运算符,则先判断栈是否为空,当栈为空时,直接将该字符添加到栈中,若栈不为空,则比较当前运算符与栈顶运算符的优先级大小,若当前运算符的优先级大,则直接将该字符添加到栈中,若当前运算符优先级小,则需要先将栈中比当前运算符优先级小的全部弹出并拼接到之前的字符串中,然后再将当前运算符添加到栈中,当遍历完字符串并且栈不为空时,将栈中剩余的字符串弹出并拼接到字符串,最后返回最终的字符串。

3.3 代码演示

static String infixToSuffix(String exp) {
        LinkedList<Character> stack = new LinkedList<>();
        StringBuilder sb = new StringBuilder(exp.length());
        /**
         * 1.遇到非运算符 直接拼串
         * 2.遇到 + - * /
         *  - 它的优先级比栈顶运算符优先级高,入栈
         *  - 否则把栈里优先级 >= 它 的都出栈,它再入栈
         * 3.遍历完成,栈里剩余运算符依次出栈
         */
        for (int i = 0; i < exp.length(); i++) {
            char c = exp.charAt(i);
            switch (c) {
                case '+':
                case '-':
                case '*':
                case '/': {
                    //比较运算符优先级
                    //栈为空,直接将当前运算符压入栈中
                    if (stack.isEmpty()) {
                        stack.push(c);
                    } else {
                        if (priority(c) > priority(stack.peek())) {
                            stack.push(c);
                        } else {
                            while (!stack.isEmpty() && priority(c) <= priority(stack.peek())) {
                                sb.append(stack.pop());
                            }
                            stack.push(c);
                        }
                    }
                    break;
                }
                default: {
                    //直接拼串
                    sb.append(c);
                    break;
                }
            }
        }
        while (!stack.isEmpty()) {
            sb.append(stack.pop());
        }
        return sb.toString();
    }
    public static int priority(char c) {
        int p = 0;
        switch(c) {
            case '*':
            case '/': {
                p = 2;
                break;
            }
            case '+':
            case '-': {
                p = 1;
                break;
            } default: {
                throw new IllegalArgumentException("不合法的符号:“" + c + "“");
            }
        };
        return p;
    }

四、中缀表达式转换为后缀表达式(有括号)

4.1 题目描述

给定一段字符串,该字符串是一段标准的中缀表达式,并且这些中缀表达式中带有括号(如:(a + b) * c、(a + b * c - d) * e、(a * b)+c),要求将该式转换为后缀表达式。

4.2 思路分析

与无括号的中缀转后缀类似,只不过多了一个左括号的优先级判断,在无括号的基础上,为左括号添加一个更低的优先级,在遍历字符串时,若遇到左括号,则直接将其添加到栈中,栈中的字符(数字或运算符)判断方法不变,若遇到右括号,则将栈中从栈顶到左括号为止(不包含左括号)的所有字符弹出并拼接到最终要返回字符串,然后再将左括号弹出(这样做的目的是不拼接左括号),之后的步骤与无括号的相同,当遍历完字符串并且栈不为空时,将栈中剩余的字符串弹出并拼接到字符串,最后返回最终的字符串。

4.3 代码演示

static String infixToSuffix(String exp) {
        LinkedList<Character> stack = new LinkedList<>();
        StringBuilder sb = new StringBuilder(exp.length());
        /**
         * 1.遇到非运算符 直接拼串
         * 2.遇到 + - * /
         *  - 它的优先级比栈顶运算符优先级高,入栈
         *  - 否则把栈里优先级 >= 它 的都出栈,它再入栈
         * 3.遍历完成,栈里剩余运算符依次出栈
         * 4.带()
         *  - 左括号直接入栈,左括号优先级设置为0
         *  - 右括号就把栈里的栈顶到左括号为止的所有运算符出栈
         */
        for (int i = 0; i < exp.length(); i++) {
            char c = exp.charAt(i);
            switch (c) {
                case '+':
                case '-':
                case '*':
                case '/': {
                    //比较运算符优先级
                    //栈为空,直接将当前运算符压入栈中
                    if (stack.isEmpty()) {
                        stack.push(c);
                    } else {
                        if (priority(c)>priority(stack.peek())) {
                            stack.push(c);
                        } else {
                            while (!stack.isEmpty() && priority(c) <= priority(stack.peek())) {
                                sb.append(stack.pop());
                            }
                            stack.push(c);
                        }
                    }
                    break;
                }
                case '(' : {
                    stack.push(c);
                    break;
                }
                case ')' : {
                    while (!stack.isEmpty() && stack.peek() != '(') {
                        sb.append(stack.pop());
                    }
                    stack.pop();
                    break;
                }
                default: {
                    //直接拼串
                    sb.append(c);
                    break;
                }
            }
        }
        while (!stack.isEmpty()) {
            sb.append(stack.pop());
        }
        return sb.toString();
    }
    public static int priority(char c) {
        int p = 0;
        switch(c) {
            case '*':
            case '/': {
                p = 2;
                break;
            }
            case '+':
            case '-': {
                p = 1;
                break;
            }
            case '(' : {
                p = 0;
                break;
            }
            default: {
                throw new IllegalArgumentException("不合法的符号:“" + c + "“");
            }
        };
        return p;
    }


相关文章
|
1月前
|
存储 算法 Java
leetcode算法题-有效的括号(简单)
【11月更文挑战第5天】本文介绍了 LeetCode 上“有效的括号”这道题的解法。题目要求判断一个只包含括号字符的字符串是否有效。有效字符串需满足左括号必须用相同类型的右括号闭合,并且左括号必须以正确的顺序闭合。解题思路是使用栈数据结构,遍历字符串时将左括号压入栈中,遇到右括号时检查栈顶元素是否匹配。最后根据栈是否为空来判断字符串中的括号是否有效。示例代码包括 Python 和 Java 版本。
|
1月前
|
机器学习/深度学习 人工智能 自然语言处理
280页PDF,全方位评估OpenAI o1,Leetcode刷题准确率竟这么高
【10月更文挑战第24天】近年来,OpenAI的o1模型在大型语言模型(LLMs)中脱颖而出,展现出卓越的推理能力和知识整合能力。基于Transformer架构,o1模型采用了链式思维和强化学习等先进技术,显著提升了其在编程竞赛、医学影像报告生成、数学问题解决、自然语言推理和芯片设计等领域的表现。本文将全面评估o1模型的性能及其对AI研究和应用的潜在影响。
43 1
|
2月前
【LeetCode-每日一题】 删除排序数组中的重复项
【LeetCode-每日一题】 删除排序数组中的重复项
23 4
|
2月前
|
索引
Leetcode第三十三题(搜索旋转排序数组)
这篇文章介绍了解决LeetCode第33题“搜索旋转排序数组”的方法,该问题要求在旋转过的升序数组中找到给定目标值的索引,如果存在则返回索引,否则返回-1,文章提供了一个时间复杂度为O(logn)的二分搜索算法实现。
24 0
Leetcode第三十三题(搜索旋转排序数组)
|
2月前
|
算法 C++
Leetcode第二十二题(括号生成)
这篇文章讨论了如何使用递归算法解决LeetCode第22题“括号生成”的问题,提供了两种C++的实现方法,目的是生成所有有效的括号组合。
21 0
Leetcode第二十二题(括号生成)
|
2月前
【LeetCode 24】225.用队列实现栈
【LeetCode 24】225.用队列实现栈
12 0
|
2月前
|
算法 C++
Leetcode第53题(最大子数组和)
这篇文章介绍了LeetCode第53题“最大子数组和”的动态规划解法,提供了详细的状态转移方程和C++代码实现,并讨论了其他算法如贪心、分治、改进动态规划和分块累计法。
70 0
|
2月前
|
存储 C++ 容器
Leetcode第二十题(有效的括号)
这篇文章介绍了如何使用栈来解决LeetCode第20题“有效的括号”问题,提供了两种方法:数组栈和容器栈,以及相应的C++代码实现。
21 0
|
2月前
|
算法
【LeetCode 23】232.用栈实现队列
【LeetCode 23】232.用栈实现队列
25 0
|
2月前
|
C++
【LeetCode 12】349.两个数组的交集
【LeetCode 12】349.两个数组的交集
19 0