算法学习day11:有效的括号,删除字符串中的所有相邻重复项,逆波兰表达式求值(Java)

有效的括号

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

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:
输入: “()”
输出: true

示例 2:
输入: “()[]{}”
输出: true

示例 3:
输入: “(]”
输出: false

示例 4:
输入: “([)]”
输出: false

示例 5:
输入: “{[]}”
输出: true

总结上面的例子,有三种情况:
1.左边多了括号,如(){}[])
2.右边多了括号,如((){}[]
3.括号不匹配(示例4可以理解成中间为[),所以不匹配)

这道题目可以用栈来完成,当我们遇到左括号时,放入对应的右括号入栈(具体原因请往下看),这样就记住了左括号的顺序。当我们遇到右括号时,如果括号有效,那么出栈的结果应该和对应的右括号一致,就达成了匹配
如:( ( ) ) [ ] { }
开始遍历,第一个为(,放一个)入栈。第二个为( ,放一个)入栈,第三个是右括号,应该和最后一个入栈的)匹配,所以消掉(出栈),第四个也是右括号,发现和即将出栈的)匹配,继续出栈

遍历完成后,如果栈为空说明括号有效。如果遇到了出栈的字符串和目标字符串不匹配(情况3)或空栈遇到右括号(情况2),或遍历完后栈不为空(情况1)即为无效

代码如下:

    public static boolean isValid(String s){
        Stack<Character> s1 = new Stack<>();
        char target;
        for (int i = 0; i < s.length(); i++) {
            target = s.charAt(i);
            if(target == '('){
                s1.push(')');
            }else if(target == '['){
                s1.push(']');
            }else if(target == '{'){
                s1.push('}');
            }else if(s1.isEmpty() || target != s1.peek()){
                return false;
            }else {
                s1.pop();
            }
        }
        return s1.isEmpty();
    }

这里要对s1.isEmpty() || target != s1.peek()做一下解释。
有前面几个if和else if做铺垫,能往下走的都是右括号,如果遍历到右括号发现栈已经空了,或者目标和栈不匹配,就直接return false。如果和目标匹配则执行s1.pop出栈。遍历完成后看s1是否为空返回结果

删除字符串中所有相邻重复项

给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。

在 S 上反复执行重复项删除操作,直到无法继续删除。

在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

示例:

输入:“abbaca”
输出:“ca”
解释:例如,在 “abbaca” 中,我们可以删除 “bb” 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 “aaca”,其中又只有 “aa” 可以执行重复项删除操作,所以最后的字符串为 “ca”。

这道题也可以用栈来解决。碰到一个字母,我们将其放入栈中,以上面的示例来看,先把a放入栈中,再把b放入栈中,到第三个字母时,发现和第二个字母(目前栈中最后一个元素)一样,就出栈。

这道题不是很难,但难点在于想到用栈的方式去解决问题。栈帮助我们记录了遍历数组当前元素时候,前一个元素是什么,这个特性就适合匹配相关的问题。
代码如下:

    public static String deleteSameChara(String s){
        Stack<Character> s1 = new Stack<>();
        for (int i = 0; i < s.length(); i++) {
            if(s1.isEmpty()){
                s1.push(s.charAt(i));
            }else if(s.charAt(i) != s1.peek()){
                s1.push(s.charAt(i));
            }else{
                s1.pop();
            }
        }
        StringBuilder str = new StringBuilder();
        while (!s1.isEmpty()){
            str.append(s1.pop());
        }
        return str.reverse().toString();
    }

    private void reverse(char[] arr){
        int left = 0;
        int right = arr.length - 1;
        char temp;
        while(left < right){
            temp = arr[left];
            arr[left] = arr[right];
            arr[right] = temp;
        }
    }

注意:这里的if(s1.isEmpty())不能和下面的else if写在一起(如果用Deque作为堆栈的话不用在意),用正常的Stack会导致空栈无法peek出结果而报错

逆波兰表达式求值

有效的运算符包括 + , - , * , / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

说明:

整数除法只保留整数部分。 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。

示例 1:
输入: [“2”, “1”, “+”, “3”, " * "]
输出: 9
解释: 该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9

示例 2:
输入: [“4”, “13”, “5”, “/”, “+”]
输出: 6
解释: 该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6

示例 3:
输入: [“10”, “6”, “9”, “3”, “+”, “-11”, " * ", “/”, " * ", “17”, “+”, “5”, “+”]
输出: 22

逆波兰表达式:是一种后缀表达式,所谓后缀就是指运算符写在后面。

平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 ) 。

该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * ) 。

逆波兰表达式主要有以下两个优点:

去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。

适合用栈操作运算:遇到数字则入栈;遇到运算符则取出栈顶两个数字进行计算,并将结果压入栈中。

代码如下:

   public static void evalRPN(String[] arr){
        int temp1;
        int temp2;
        Stack<Integer> s1 = new Stack<>();
        for (int i = 0; i < arr.length; i++) {
            for (String s : arr) {
                if("+".equals(s)){
                    temp1 = s1.pop();
                    temp2 = s1.pop();
                    s1.push(temp1 + temp2);
                }else if("-".equals(s)){
                    temp1 = s1.pop();
                    temp2 = s1.pop();
                    s1.push(temp2 - temp1);
                }else if("*".equals(s)){
                    temp1 = s1.pop();
                    temp2 = s1.pop();
                    s1.push(temp1 * temp2);
                }else if("/".equals(s)){
                    temp1 = s1.pop();
                    temp2 = s1.pop();
                    s1.push(temp2 / temp1);
                }else{
                    s1.push(Integer.valueOf(s));
                }
            }
        }
    }

这里需要注意的是-和/,它们是有先后顺序的,按照入栈的顺序,temp1应该是除(减)数,temp2才是被除(减)数

  • 34
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值