算法之栈

介绍

157889411448713

栈是一种线性结构,它只能从一端添加元素,也只能从一端取出元素(这一端称之为栈顶)。

Stack这种数据结构用途很广泛,在计算机的使用中,大量的运用了栈,比如编译器中的词法分析器、Java虚拟机、软件中的撤销操作(Undo)、浏览器中的回退操作,编译器中的函数调用实现等等。

接口 说明 复杂度
void push(E e) 向栈中加入元素 O(1) 均摊
E pop() 弹出栈顶元素 O(1) 均摊
E peek() 查看栈顶元素 O(1)
int getSize() 获取栈中元素个数 O(1)
boolean isEmpty() 判断栈是否为空 O(1)

说明:push和pop操作在最后面进行,有可能触发resize,但均摊来算是O(1)的。

面试题

有效的括号

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

说明:有效字符串需满足

  • 左括号必须用相同类型的右括号闭合。

  • 左括号必须以正确的顺序闭合。

  • 示例1

    输入:()

    输出:true

    示例2

    输入:()[]{}

    输出:true

    示例3

    输入:(]

    输出:false

    示例4

    输入:([)]

    输出:false

    示例5

    输入:{[]}

    输出:true

解题思路

这道题让我们验证输入的字符串是否为括号字符串,包括大括号,中括号和小括号。

这里我们使用

  • 遍历输入字符串
  • 如果当前字符为左半边括号时,则将其压入栈中
  • 如果遇到右半边括号时,分类讨论:
  • 1)如栈不为空且为对应的左半边括号,则取出栈顶元素,继续循环
  • 2)若此时栈为空,则直接返回 false
  • 3)若不为对应的左半边括号,反之返回 false

157889417054455

代码实现

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

用两个栈实现队列

用两个栈来实现一个队列,完成队列的 Push 和 Pop 操作。

解题思路

in 栈用来处理入栈(push)操作,out 栈用来处理出栈(pop)操作。一个元素进入 in 栈之后,出栈的顺序被反转。当元素要出栈时,需要先进入 out 栈,此时元素出栈顺序再一次被反转,因此出栈顺序就和最开始入栈顺序是相同的,先进入的元素先退出,这就是队列的顺序。

  • push 元素时,始终是进入栈,pop 和 peek 元素时始终是走出栈。
  • pop 和 peek 操作,如果出栈为空,则需要从入栈将所有元素移到出栈,也就是调换顺序,比如开始push的顺序是 3-2-1,1 是最先进入的元素,则到出栈的顺序是 1-2-3,那 pop 操作拿到的就是 1,满足了先进先出的特点。
  • pop 和 peek 操作,如果出栈不为空,则不需要从入栈中移到数据到出栈。

157889418862433

代码实现

    static Stack<Integer> stack1 = new Stack<>();
    static Stack<Integer> stack2 = new Stack<>();

    public static void appendTail(int item) {
        stack1.push(item);
    }

    public static int deleteHead() {
        if (!stack2.isEmpty()) {
            return stack2.pop();
        }
        while (!stack1.isEmpty()) {
            stack2.push(stack1.pop());
        }
        return stack2.pop();
    }

栈的压入、弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列 1,2,3,4,5 是某栈的压入顺序,序列 4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

解题思路

借用一个辅助的栈,遍历压栈顺序,先将 第一个放入栈中,这里是 1,然后判断栈顶元素是不是出栈顺序的第一个元素,这里是 4,很显然 1≠4 ,所以需要继续压栈,直到相等以后开始出栈。

出栈一个元素,则将出栈顺序向后移动一位,直到不相等,这样循环等压栈顺序遍历完成,如果辅助栈还不为空,说明弹出序列不是该栈的弹出顺序。

代码实现

    public static boolean isPopOrder(int[] pPush, int[] pPop) {
        boolean bPossible;
        if (pPush.length == 0 || pPop.length == 0) {
            return false;
        }
        Stack<Integer> stack = new Stack<>();
        int popIndex = 0;
        for (int push : pPush) {
            stack.push(push);
            while (!stack.isEmpty() && stack.peek() == pPop[popIndex]) {
                stack.pop(); 
                ++popIndex;
            }
        }
        bPossible = stack.isEmpty() && pPop.length == popIndex;
        return bPossible;
    }

包含 min 函数的栈

定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的 min 函数(时间复杂度应为O(1))。

解题思路

使用两个 stack,一个作为数据栈,另一个作为辅助栈。其中 数据栈 用于存储所有数据,而 辅助栈 用于存储最小值。

  1. 入栈的时候:首先往空的数据栈里压入数字 3 ,此时 3 是最小值,所以把最小值压入辅助栈。接下来往数据栈里压入数字 4 。由于 4 大于之前的最小值,因此只要入数据栈,此时辅助栈把记录最小值栈的栈顶元素再压一次(辅助栈保持跟数据站一样的元素数量)。
  2. 出栈的时候:数据栈进行了弹栈操作,那么记录最小值元素的辅助栈同样也进行弹栈操作即可。
  3. 获得栈顶元素的时候:直接返回数据栈的栈顶元素。
  4. 栈最小元素:直接返回辅助栈的栈顶元素,但是不要弹出。

代码实现

    static Stack<Integer> data = new Stack<>();
    /**
     * 辅助栈,data栈中每次进入一个元素,min辅助栈就存放当前data栈中的最小元素
     */
    static Stack<Integer> min = new Stack<>();

    public static void push(int item) {
        //直接调用data的push方法,将元素压入栈中。
        data.push(item);
        //如果进栈的元素小于当前的最小值就把进栈元素的值赋值该minValue
        if (min.size() == 0 || item < min.peek()) {
            min.push(item);
        } else {
            //将当前的最小值压入minStack栈中
            min.push(min.peek());
        }
    }

    public static void pop() {
        if (data.size() > 0 && min.size() > 0) {
            data.pop();
            min.pop();
        }
    }

    public static int min() {
        if (data.size() > 0 && min.size() > 0) {
            return min.peek();
        }
        return 0;
    }

    public static int top() {
        if (data.size() > 0) {
            return data.peek();
        }
        return 0;
    }
posted @   Faetbwac  阅读(43)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示