java算法系列,第十四篇:栈的应用(括号配对,逆波兰表达式(后缀表达式),中缀转后缀)

接下来,我们用栈来解决几个问题。

  • 括号配对

leetcode原题:

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

示例 1:

输入:s = "()"
输出:true

示例 2:

输入:s = "()[]{}"
输出:true

示例 3:

输入:s = "(]"
输出:false

提示:

  • 1 <= s.length <= 104
  • s 仅由括号 '()[]{}' 组成

解题思路:

  • 遇到左括号就把与它对应的右括号压入栈
  • 遇到右括号就从栈里面弹出栈顶与之比较,一致则继续,不一致就直接return false
  @Test
    @DisplayName("括号配对算法")
    public void test37()
    {
        //有效括号
        System.out.println(isValid("()"));
        System.out.println(isValid("()[]{}"));
        System.out.println(isValid("{[]}"));
        System.out.println("------------");
        //无效括号
        System.out.println(isValid("(]"));
        System.out.println(isValid("([)]"));
        System.out.println(isValid("]"));
        System.out.println("-----------------");
        System.out.println(isValid(")("));
        System.out.println(isValid("]"));

    }
    /**
     * 括号配对算法
     * @param s 字符串
     * @return  是否有效
     */
    public boolean isValid(String s)
    {
        ArrayStack<Character> stack = new ArrayStack<>(s.length());//创建一个容量为字符串长度的栈
        //遍历字符串
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);

            switch (c) {//如果是左括号,就把对应的右括号入栈
            case '(':
                stack.push(')');
                break;
            case '[':
                stack.push(']');
                break;
            case '{':
                stack.push('}');
                break;
            default://如果是右括号,就把栈顶元素出栈,然后判断是否相等
                if (!stack.isEmpty() && c == stack.peek())
                {
                    stack.pop();
                }
                else
                {
                    return false;
                }
            }
        }

        if (stack.isEmpty())//如果栈为空,就说明括号全部配对成功
        {
            return true;
        }
        else
        {
            return false;
        }
    }

  • 逆波兰表达式

逆波兰表达式(Reverse Polish Notation,RPN),也称为后缀表达式,是一种数学和计算机科学中的表达式表示方法。在逆波兰表达式中,操作符位于操作数的后面,这种表示方法可以消除括号,使运算顺序更加清晰。

在逆波兰表达式中,每个运算符都跟随着其操作数,使得解析表达式变得更加直观。这种表示法的一个重要特点是,操作符总是作用于最近的操作数,避免了操作符优先级和括号的问题。

例如,中缀表达式 "3 + 4" 在逆波兰表达式中表示为 "3 4 +"。更复杂的表达式也可以用逆波兰表达式表示,如 "(5 + 2) * 3" 可以表示为 "5 2 + 3 *"。

逆波兰表达式在计算器、编程语言的解释器以及某些计算机科学应用中被广泛使用,因为它提供了一种直观且不需要解析括号的方式来表示和计算算术表达式。

解题思路:

  • 遇到数字就把数字压入栈,遇到运算符就弹出两个数字进行相应运算
 @Test
    @DisplayName("后缀表达式")
    public void test38()
    {
        String[] suffixExpression = {"2","1","+","3","*"};
        System.out.println(calculate(suffixExpression));
        String[] suffixExpression2 = {"4","13","5","/","+"};
        System.out.println(calculate(suffixExpression2));
    }
    /**
     * 后缀表达式
     * @param tokens 后缀表达式
     * @return       计算结果
     */
    public int calculate(String[] tokens)
    {
        LinkedList<Integer> stack = new LinkedList<>();
        for (String t : tokens)//遍历字符数组
        {
            switch (t){
            case "+" :
                stack.push(stack.pop() + stack.pop());
                break;
            case "-" :
                stack.push(-stack.pop() + stack.pop());//注意顺序,后入先出
                break;
            case "*" :
                stack.push(stack.pop() * stack.pop());
                break;
            case "/" :
                int b= stack.pop();
                int a= stack.pop();
                stack.push(a / b);
                break;
            default:
                stack.push(Integer.parseInt(t));
                break;
            }
        }
        return stack.pop();//返回栈顶元素
    }

  • 中缀转后缀

众所周知,java最后会被jvm转成汇编代码,而汇编语言中使用的就是后缀表达式

那么用Java怎么把中缀转成后缀表达式形式呢?

解题思路:

  • 遇到数字字符就直接拼接到StringBuilder中去
  • 遇到运算符就先判断其优先级
  • 如果栈为空或当前运算符高于栈顶的运算符,就直接入栈
  • 若当前运算符低于或等于栈顶运算符,就循环弹出栈顶元素,并拼接到StringBuilder
  • 当遍历完输入的字符串后,循环弹出栈顶元素,并拼接到StringBuilder
  • 遇到括号这种辅助原算符时为了不扰乱以上流程,要做特殊处理:
  • 1.将左括号的优先级定义为最低
  • 2.遇到左括号直接入栈
  • 3遇到右括号弹出左括号前面的使用栈顶元素,拼接到StringBuilder,然后弹出左括号
@Test
    @DisplayName("中缀表达式转后缀表达式")
    public void test39()
    {
        String expression = "1+((2+3)*4)-5";//123+4*+5-
        System.out.println(infixToSuffix(expression));
        expression = "1+2*3";
        System.out.println(infixToSuffix(expression));
    }


/**
     * 运算符优先级
     * @param c 运算符
     * @return  优先级
     */
    private int priority(char c)
    {
        switch (c) {
        case '(':
            return 0;
        case '+','-':
            return 1;
        case '*', '/':
            return 2;
        default:
            throw new IllegalArgumentException("非法运算符"+c);
        }
    }



 /**
     * 中缀表达式转后缀表达式
     * @param expression 中缀表达式
     * @return           后缀表达式
     */
    public String infixToSuffix(String expression)
    {
        LinkedList<Character> stack = new LinkedList<>();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < expression.length(); i++)
        {
            char c = expression.charAt(i);
            switch (c){
                case '+','-','*','/' ://这是增强型的case写法,要java14以上才支持
                    if (stack.isEmpty())//如果栈为空,就把运算符入栈
                    {
                        stack.push(c);
                    }
                    else
                    {
                        if (priority(c) > priority(stack.peek()))//如果栈顶运算符的优先级小于当前运算符的优先级,就把当前运算符入栈
                        {
                            stack.push(c);
                        }
                        else
                        {
                            while (!stack.isEmpty() &&  priority(stack.peek()) >= priority(c))//如果栈顶运算符的优先级大于等于当前运算符的优先级,就把栈顶运算符出栈并拼接
                            {
                                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();
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值