算法打卡day10|栈与队列篇02|Leetcode 20. 有效的括号、1047. 删除字符串中的所有相邻重复项、150. 逆波兰表达式求值

在做题之前说明

ArrayDequeStack

在Java中,推荐使用ArrayDeque而不是Stack来实现栈结构,主要基于以下几点原因:

  1. 性能ArrayDeque是基于数组实现的,而Stack是基于Vector实现的。Vector在所有的方法上都加了同步处理,以保证线程安全,这导致了Stack在单线程环境中的性能较低。相比之下,ArrayDeque是非线程安全的,没有同步开销,因此其性能更优。

  2. 设计ArrayDeque设计上就是为了提供双端队列(deque)的功能,而栈(stack)只是双端队列的一种特殊用法,即只在一端(队尾)添加或删除元素。ArrayDeque内部优化了这种使用方式,使得作为栈使用时效率更高

  3. 功能性:虽然Stack提供了栈的基本操作(如pushpoppeek等),但ArrayDeque作为Deque接口的实现,除了可以用作栈,还可以用作队列,提供了更多的灵活性

  4. 空值处理ArrayDeque不允许插入null值,这有助于避免潜在的空指针异常。而Stack允许在栈中插入null值,这可能导致运行时错误

  5. API更新:随着Java集合框架的发展,Stack被视为遗留类,其功能在Deque接口及其实现类(如ArrayDeque)中得到了更好的支持和实现。因此,从最佳实践的角度来看,建议使用新的集合框架API。

ArrayDeque 和 Deque<Integer> 

Deque<Integer> 是一个双端队列泛型接口,而 ArrayDeque 是 Deque 接口的一个具体实现,它使用数组来存储元素。

当声明一个变量为 Deque<Integer> 类型时,在指定该变量将持有实现了 Deque 接口的对象。这样做的好处是,可以轻松地更换不同的实现,比如 ArrayDequeLinkedList 或者其他任何 Deque 的实现,而不会影响到使用该变量的代码。这提供了更高的灵活性和可维护性

Deque<Integer> deque = new ArrayDeque<>();
// 或者
Deque<Integer> deque = new LinkedList<>();

在上面的代码中,deque 可以指向任何 Deque 接口的实现。如果决定改变实现,只需要更改构造函数调用的类即可。

另一方面,当直接使用 ArrayDeque 时,是在指定使用这个特定的双端队列实现:在这种情况下,arrayDeque 变量只能引用 ArrayDeque 的实例,不能引用其他实现了 Deque 接口的类

ArrayDeque<Integer> arrayDeque = new ArrayDeque<>();

使用场景

  • 当需要双端队列的功能并且关注性能时,通常会选择 ArrayDeque。由于它是基于数组实现的,它在随机访问和栈(后进先出)操作上表现得非常好。

  • 当需要双端队列的功能并且想要保持最大的通用性时,可以声明一个 Deque<Integer> 类型的变量。这样,你可以在不改变使用代码的情况下,自由地切换不同的实现,比如出于测试目的或者为了适应不同的运行时环境。

  • 当需要一个既可以作为队列又可以作为双端队列使用的数据结构时,使用 LinkedList 实现的 Deque<Integer> 可能更合适,因为 LinkedList 同时实现了 List 和 Deque 接口。

  • ArrayDeque会比LinkedList在除了删除元素这一点外会快一点

算法题

Leetcode  20. 有效的括号

题目链接:20. 有效的括号

大佬视频讲解:有效的括号视频讲解

个人思路

这种括号匹配肯定是用栈结构; 可以先入右括号,然后只需要比较当前元素和栈顶相不相等就可以了!

解法

当遇到一个左括号时,将这个左括号放入栈顶。当遇到一个右括号时,取出栈顶的左括号并判断它们是否是相同类型的括号。如果不是相同的类型,或者栈中并没有左括号,那么字符串 s无效,返回 False。遍历结束后,如果栈中没有左括号,说明将字符串中的所有左括号闭合,返回 True,否则返回 False 。

为了快速判断括号的类型,可以使用哈希表存储每一种括号。哈希表的为右括号,为相同类型的左括号。
 

class Solution {
    public boolean isValid(String s) {
        int len= s.length();
        if(len%2==1){ return false;}//有效字符串的长度一定为偶数,若为奇数则直接返回false

        Map<Character , Character > pairs =new HashMap<Character,Character>(){{
            put(')','(');//键为右括号,值为相同类型的左括号
            put('}','{');
            put(']','[');
        }};
        Deque<Character> stack=new LinkedList<Character>();
        //遇到一个右括号时,将一个相同类型的左括号闭合
        for(int i=0;i<len;i++){
            char ch=s.charAt(i);//遍历字符串
            if(pairs.containsKey(ch)){//碰到右括号时
                //栈为空或者没有匹配的左括号 返回false
                if(stack.isEmpty() || stack.peek() !=pairs.get(ch)){
                    return false;
                }
                stack.pop();//否则闭环
            }else { stack.push(ch);}//左括号入栈
        }
        return stack.isEmpty();//栈若为空说明全部括号都闭合了
}
    
}

时间复杂度:O(n);(遍历字符串)

空间复杂度:O(n);(使用到一个栈和一个常量字符集6种括号)

Leetcode 1047. 删除字符串中的所有相邻重复项

题目链接:1047. 删除字符串中的所有相邻重复项

大佬视频讲解:删除字符串中的所有相邻重复项视频讲解

个人思路

就是消消乐,上上面这题原理一样,栈结构非常合适,进一个元素,判断一个元素,如果能消除则弹出,否则放入

解法

注意:因为从栈里弹出的元素是倒序的,所以再对字符串进行反转一下,就得到了最终的结果。

class Solution {
    public String removeDuplicates(String s) {
        ArrayDeque<Character> stack=new ArrayDeque<>();

        char ch;
        for(int i=0;i<s.length();i++){//遍历字符串s
            ch=s.charAt(i);
            if(stack.isEmpty() || ch!=stack.peek()){//若栈为空或者栈中元素与ch不同则放入
                stack.push(ch);
            }else {
                stack.pop();
            }
        }
        String result="";//结果字符串
        while(!stack.isEmpty()){//因为是栈,所以需要倒序弹出
            result=stack.pop()+ result;
        }
        return result;

    }
}

时间复杂度:O(n);(一个遍历,一个弹出结果字符串 2n)

空间复杂度:O(n);(使用了数组队列)

Leetcode 150. 逆波兰表达式求值

题目链接:150. 逆波兰表达式求值

大佬视频讲解:逆波兰表达式求值视频讲解

个人思路

也是消消乐,栈结构搞定它;遍历判断如果是运算符号则弹出两个数据做运算后再放入,如果是数字则直接放入。

解法

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

平常使用的算式则是一种中缀表达式,如 ( 7 + 7 ) * ( 8+ 8 ) 。该算式的逆波兰表达式写法为 ( ( 7 7 + ) ( 8 8 + ) * ) 。

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

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

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

用逆波兰表达式后计算机可以利用栈来顺序处理,不需要考虑优先级了。也不用回退了, 所以后缀表达式对计算机来说是非常友好的。

class Solution {
    public int evalRPN(String[] tokens) {
        Deque<Integer> stack=new LinkedList();

        for(String s:tokens){
            if(s.equals("+")){
                stack.push(stack.pop()+stack.pop());
            }else if(s.equals("-")){

                stack.push(-stack.pop()+stack.pop());//因为是栈,顺序是反的,减法放前面
            }
            else if(s.equals("*")){
                stack.push(stack.pop()*stack.pop());
            }

            else if(s.equals("/")){//要先弹出,再t2除t1,不然运算顺序不正确
                int t1=stack.pop();
                int t2=stack.pop();
                stack.push(t2 / t1);
            }

            else {
                stack.push(Integer.valueOf(s));
            }
        }
        return stack.pop();

    }
}

时间复杂度:O(n);(遍历字符数组)

空间复杂度:O(n);(栈空间)

以上是个人的思考反思与总结,若只想根据系列题刷,参考卡哥的网址代码随想录算法官网

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值