精解括号匹配问题与极致栈设计:揭开最大栈和最小栈的奥秘

简介: 精解括号匹配问题与极致栈设计:揭开最大栈和最小栈的奥秘


最大栈和最小栈是极致栈的两个重要变种。最大栈用于存储当前匹配的最大值,而最小栈用于存储当前匹配的最小值。

括号匹配问题

这个问题我们来看力扣20题的描述:

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

有效字符串需满足:

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

示例 1:

输入:s = “()”

输出:true

示例 2:

输入:s = “()[]{}”

输出:true

示例 3:

输入:s = “(]”

输出:false

对于这个题我们有两种解决思路:

1.我们用哈希表把所有符号先存储起来,左边符号作key,右边符号作value。遍历字符串的时候,遇见左边符号就入栈,遇见右边符号就与栈顶的符号进行比较,不匹配就返回false。

public boolean isValid(String s) {
        //获取字符串长度
        int n = s.length();
        //如果字符串长度为奇数,则返回false
        if (n % 2 == 1) {
            return false;
        }
        //创建一个HashMap,用于存储字符串中的括号
        Map<Character, Character> map = new HashMap<>();
        map.put('[', ']');
        map.put('(', ')');
        map.put('{', '}');
        //创建一个栈,用于存储字符串中的括号
        Stack<Character> stack = new Stack<>();
        //遍历字符串中的每一个字符
        for (int i = 0; i < s.length(); i++) {
            char item = s.charAt(i);
            //如果字符串中的字符在HashMap中存在,则将其压入栈中
            if (map.containsKey(item)) {
                stack.push(item);
            } else {
                //如果栈不为空,则弹出栈顶元素,如果弹出的元素与当前字符串中的字符不匹配,则返回false
                if (stack.isEmpty() == false) {
                    char pop = stack.pop();
                    if (map.get(pop) != item) {
                        return false;
                    }
                } else {
                    return false;
                }
            }
        }
        //如果栈为空,则返回true,否则返回false
        return stack.isEmpty();
    }
  1. 单纯的使用栈,如果遇见左边符号直接压入栈中,遇见右边的符号是先判断栈是否为空,为空则返回false,不为空则弹出栈顶元素,如栈顶元素不为相匹配的左边符号则直接返回false,最后元素遍历完返回栈是否为空。
public boolean isValid(String s) {
        int n = s.length();
        // 如果字符串长度为奇数,则直接返回false
        if (n % 2 == 1) {
            return false;
        }
        // 创建一个栈
        Deque<Character> stack = new LinkedList<>();
        // 遍历字符串
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            // 如果当前字符为左括号,则将其压入栈中
            if (c == '(' | c == '[' || c == '{') {
                stack.push(c);
            // 如果当前字符为右括号,则从栈中弹出一个元素,如果弹出的元素与当前字符不匹配,则返回false
            } else if (c == '}' || c == ']' || c == ')') {
                if (stack.isEmpty()) {
                    return false;
                }
                char top = stack.pop();
                if ((top != '(' && c == ')') || (top != '{' && c == '}') || (top != '[' && c == ']')) {
                    return false;
                }
            }
        }
        // 如果栈为空,则返回true,否则返回false
        return stack.isEmpty();
    }

最小栈

我们来看力扣155题的描述:

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

实现 MinStack 类:

  • MinStack() 初始化堆栈对象。
  • void push(int val) 将元素val推入堆栈。
  • void pop() 删除堆栈顶部的元素。
  • int top() 获取堆栈顶部的元素。
  • int getMin() 获取堆栈中的最小元素。

这个题通俗的理解就是给栈提供一个能获取最小元素的方法并且要在常数时间内。

我们可以设计一个辅助栈,与元素栈同步插入与删除,用于存储每个元素入栈时的最小值(也就是说在辅助栈中我们每次插入的是元素栈中的最小值)

  • 当一个元素要入栈时,我们取当前辅助栈的栈顶存储的最小值,与当前要入栈的元素中的最小值插入辅助栈中。
  • 当一个元素要出栈时,我们把辅助栈的栈顶元素也一并弹出。
    我们来看具体实现代码:
class MinStack {
    // 定义两个双端队列,分别存放输入的值和当前的最小值
    Deque<Integer> xStack;
    Deque<Integer> minStack;
    public MinStack() {
        // 初始化双端队列
        xStack = new LinkedList<>();
        minStack = new LinkedList<>();
        // 第一个最小值设置为最大值
        minStack.push(Integer.MAX_VALUE);
    }
    public void push(int val) {
        // 输入一个值
        xStack.push(val);
        // 将当前的最小值和输入的值比较,取较小的值
        minStack.push(Math.min(minStack.peek(), val));
    }
    public void pop() {
        // 弹出双端队列的最后一个值
        xStack.pop();
        minStack.pop();
    }
    public int top() {
        // 返回双端队列的最后一个值
        return xStack.peek();
    }
    public int getMin() {
        // 返回双端队列的最小值
        return minStack.peek();
    }
}

最大栈

跟最小栈实现方法类似寻找最大值。

需要注意的就是最后一个方法,弹出最大值,具体就是拿到最大元素,然后在数字栈中把最大值以上的元素全部弹出存储在新建的栈中,然后弹出最大值,最后把新建的栈中的元素重新压入数字栈中。

由于力扣最大栈是VIP题目,我们可以尝试一下牛客的最大栈问题

class MaxStack {
    // 定义两个栈,一个用来存储数字,另一个用来存储最大值
    Deque<Integer> xStack;
    Deque<Integer> maxStack;
    public MaxStack() {
        // 初始化两个栈
        xStack = new LinkedList<>();
        maxStack = new LinkedList<>();
    }
    public void push(int val) {
        // 获取当前最大值,如果栈为空,则最大值为当前值
        int max = maxStack.isEmpty() ? val : maxStack.peek();
        // 比较当前值和最大值,取最大值
        max = max > val ? max : val;
        // 将值和最大值分别压入栈中
        xStack.push(val);
        maxStack.push(max);
    }
    public int pop() {
        // 弹出最大值栈顶元素
        maxStack.pop();
        // 弹出数字栈顶元素
        return xStack.pop();
    }
    public int top() {
        // 返回数字栈顶元素
        return xStack.peek();
    }
    public int peekMax() {
        // 返回最大值栈顶元素
        return maxStack.peek();
    }
    public int popMax() {
        // 获取最大值栈顶元素
        int max = peekMax();
        // 创建一个栈
        Stack<Integer> stack = new Stack<>();
        // 当栈顶元素不等于最大值时,将栈顶元素压入栈中
        while (top() != max) {
            stack.push(pop());
        }
        // 弹出数字栈顶元素
        pop();
        // 将栈中的元素弹出,压入数字栈中
        while (!stack.isEmpty()) {
            push(stack.pop());
        }
        // 返回最大值
        return max;
    }
}
相关文章
|
22天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
104 9
|
13天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
21 1
|
15天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
18天前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
20天前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
47 4
|
24天前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
1月前
数据结构(栈与列队)
数据结构(栈与列队)
20 1
|
1月前
|
存储 JavaScript 前端开发
为什么基础数据类型存放在栈中,而引用数据类型存放在堆中?
为什么基础数据类型存放在栈中,而引用数据类型存放在堆中?
71 1
|
1月前
【数据结构】-- 栈和队列
【数据结构】-- 栈和队列
17 0
|
1月前
探索顺序结构:栈的实现方式
探索顺序结构:栈的实现方式