LeetCode刷题 --- 栈

news2024/11/17 12:48:10

栈(stack)是一种用于存储数据的简单数据结构。栈一个有序线性表,只能在表的一端(PS:栈顶)执行插人和删除操作。最后插人的元素将被第一个删除。所以,栈也称为后进先出(Last In First Out,LIFO)或先进后出(First In Last Out,FILO)线性表。
 

栈的几种常用方法。


public interface Stack<E> extends Iterable<E> {
    
    //获取栈的size大小
    public int size();
    
    //判断栈是否为空
    public boolean isEmpty();
 
    //入栈 进栈一个元素 在线性表的表尾添加一个元素
    public void push(E element);
 
    //出栈 弹出一个元素 在线性表的表尾删除一个元素
    public E pop();
 
    //查看当前栈顶元素 并不是移除 查看线性表中最后一个元素
    public E peek();
 
    //对当前栈进行清空
    public void clear();

}

练习题:

20 有效的括号

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

解题1:

很明显的一种栈的使用。每次入栈的时候,判断当前元素和栈顶元素是否配对,如果配对,则弹出,如果不配对,则继续入栈。最后栈元素为空,则true,否则,为false。

class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        for (int i = 0; i < s.length(); i++) {
            if (stack.size() == 0 || !isPair(stack.peek(), s.charAt(i))) {
                stack.push(s.charAt(i));
            } else {
                stack.pop();
            }
        }
        return stack.size() == 0;
    }

    public static boolean isPair(char x, char y) {
        if (x == '(' && y == ')') {
            return true;
        }
        if (x == '[' && y == ']') {
            return true;
        }
        if (x == '{' && y == '}') {
            return true;
        }
        return false;
    }
}

解题2:

遇到左括号,则对应的右括号入栈,遇到右括号,则比较与栈顶元素是否一致,一致则出栈,继续,否则直接返回false。

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

150 后缀表达式,逆波兰表达式求值。

给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。

public int evalRPN1(String[] tokens) {
    Stack<Integer> stack = new Stack<>();
    for (String token : tokens) {
        if ("+".equals(token)) {
            Integer b = stack.pop();
            Integer a = stack.pop();
            stack.push(a + b);
        } else if ("-".equals(token)) {
            Integer b = stack.pop();
            Integer a = stack.pop();
            stack.push(a - b);
        } else if ("*".equals(token)) {
            Integer b = stack.pop();
            Integer a = stack.pop();
            stack.push(a * b);
        } else if ("/".equals(token)) {
            Integer b = stack.pop();
            Integer a = stack.pop();
            stack.push(a / b);
        } else {
            stack.push(Integer.parseInt(token));
        }
    }
    return stack.peek();
}

中缀表达式转后缀表达式:

1、简单版本,没有括号,只有加减乘除

解题思路:

  1. 定义一个栈,用于存放运算符
  2. 定义一个新数组,长度和给定的中缀表达式数组长度一样,然后遍历给定的数组
  3. 如果遇到的不是运算符,那么直接存在新数组中
  4. 如果栈为空,或者栈顶的运算符优先级小于当前运算符优先级,那么当前运算符入栈(例如,当前栈顶为+,如果遇到*,那么就直接将*入栈)
  5. 如果运算符优先级大于等于当前运算符优先级,那么弹出栈顶元素,存入新数组中,然后循环这一步骤
  6. 跳出5的循环后,将当前运算符入栈。
  7. 遍历完成后,对当前栈进行操作,不断将栈顶元素弹出,添加到新数组中。
    public static String[] Infix2Suffix(String[] tokens) {
        Stack<String> stack = new Stack<>();
        String[] res = new String[tokens.length];

        int count = 0;

        for (String token : tokens) {
            int fix = fix(token);
            if (fix == 0) {
                res[count++] = token;
            } else if (stack.isEmpty() || fix > fix(stack.peek())) {
                stack.push(token);
            } else {
                while (!stack.isEmpty() && fix <= fix(stack.peek())) {
                    String pop = stack.pop();
                    res[count++] = pop;
                }
                stack.push(token);
            }
        }

        while (!stack.isEmpty()) {
            res[count++] = stack.pop();
        }

        return res;
    }

    public static int fix(String str) {
        if ("+".equals(str) || "-".equals(str)) {
            return 1;
        } else if ("*".equals(str) || "/".equals(str)) {
            return 2;
        } else {
            return 0;
        }
    }

2、升级版本,带括号

解题思路:

和上述不带括号的差不多,区别就是:

  1. 遇到左括号,直接入栈
  2. 遇到右括号,把左括号之前的运算符全部出栈,添加到新数组里面,直到遇到左括号,把左括号弹出丢弃
    /**
     * 中缀转后缀,带括号版本
     */
    public static String[] Infix2Suffix1(String[] tokens) {
        Stack<String> stack = new Stack<>();
        String[] res = new String[tokens.length];

        int count = 0;

        for (String token : tokens) {
            int fix = fix(token);
            if (fix == 0) {
                if (token.equals("(")) {
                    stack.push(token);
                } else if (token.equals(")")) {
                    while (!stack.isEmpty() && !stack.peek().equals("(")) {
                        String pop = stack.pop();
                        res[count++] = pop;
                    }
                    if (!stack.isEmpty()) {
                        stack.pop();
                    }
                } else {
                    res[count++] = token;
                }
            } else if (stack.isEmpty() || fix > fix(stack.peek())) {
                stack.push(token);
            } else {
                while (!stack.isEmpty() && fix <= fix(stack.peek())) {
                    String pop = stack.pop();
                    res[count++] = pop;
                }
                stack.push(token);
            }
        }

        while (!stack.isEmpty()) {
            res[count++] = stack.pop();
        }

        return res;
    }

232. 用栈实现队列

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(pushpoppeekempty

解题思路:

  1. 题目要求用两个栈实现队列的操作,即先入先出
  2. 定义两个栈,一个栈1专门用于存储数据,执行push操作,一个栈2用来执行出队列操作,执行pop和peek操作。
  3. 栈1,只负责执行push,如果遇到添加操作,就把值push到栈1中即可。
  4. 出队列的时候,考虑需要把最初添加进入的元素给弹出来,一个栈显然执行不了这个操作,因此栈2 作为辅助栈,执行出栈前,先把栈1中所有的元素逐个弹出来,添加到栈2中,这样就相当于把栈1中的元素顺序调换了,此时,栈2顶部的元素,就是最初添加的元素,可以直接pop出来。
  5. 后续执行操作时,栈2只要不为空,那么栈顶的元素就是当前元素中,最先添加的元素,直接pop就好。
  6. 判空:两个栈都为空,队列才为空。

 图示,先push1,2,3,然后peek,栈1内元素转到栈2,后面只要栈2不为null,就可以执行pop和peek操作,如果为空,在吧元素从栈1转到栈2 就可以了。

class MyQueue {

    Stack<Integer> stack;

    Stack<Integer> reStack;

    public MyQueue() {
        stack = new Stack<>();
        reStack = new Stack<>();
    }

    public void push(int x) {
        stack.push(x);
    }

    public int pop() {
        if (reStack.isEmpty()) {
            while (!stack.isEmpty()) {
                reStack.push(stack.pop());
            }
        }
        return reStack.pop();
    }

    public int peek() {
        if (reStack.isEmpty()) {
            while (!stack.isEmpty()) {
                reStack.push(stack.pop());
            }
        }
        return reStack.peek();
    }

    public boolean empty() {
        return reStack.isEmpty() && stack.isEmpty();
    }
}

225. 用队列实现栈

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(pushtoppop 和 empty)。实现 MyStack 类;

解题思路:

其实和上面的解题思路差不多,两个队列模拟栈的操作,就是将先入先出改成后入先出

  1. 定义两个栈,q1,q2。
  2. 添加元素时,先把元素放在q2中,然后,把q1里面的元素全部转移到q2中,这时,最新添加的元素在q2中相当于最一开始添加的元素了,因此可以实现后入先出。
  3. 此时在交换q1和q2的指针。保证所有元素都在q1里面。每次取元素,从q1取即可。

如图,先添加1,后添加2

public class MyStack {

    Queue<Integer> queue1;

    Queue<Integer> queue2;

    Queue<Integer> temp;

    public MyStack() {
        queue1 = new ArrayDeque<>();
        queue2 = new ArrayDeque<>();
    }

    public void push(int x) {
        queue2.offer(x);
        while (!queue1.isEmpty()) {
            queue2.offer(queue1.poll());
        }
        temp = queue1;
        queue1 = queue2;
        queue2 = temp;
    }

    public int pop() {
        return queue1.poll();
    }

    public int top() {
        return queue1.peek();
    }

    public boolean empty() {
        return queue1.isEmpty() && queue2.isEmpty();
    }
}

解题思路2:

上述用两个队列实现了栈的后入先出(两个队列做数据转移),其实一个队列就可以完成:

  1. 定义一个队列,和一个计数器,计算器用于计算当前队列中元素的个数
  2. 添加元素时,将新元素添加到队列尾部,然后做一个循环,把这个新元素之前的元素全部poll出来从新添加到这个队列中,那么,新添加的元素就会到队列的头部,这样就可以poll或者peek了
  3. 怎么解决循环的次数呢?只需要定义一个计数器,记录当前队列中有几个元素就好了。
public class MyStack {
    Queue<Integer> queue;

    int size;

    public MyStack() {
        queue = new ArrayDeque<>();
        size = 0;
    }

    public void push(int x) {
        queue.offer(x);
        for (int i = 0; i < size; i++) {
            queue.offer(queue.poll());
        }
        size++;
    }

    public int pop() {
        size--;
        return queue.poll();
    }

    public int top() {
        return queue.peek();
    }

    public boolean empty() {
        return size == 0;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/545697.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI在狂飙,ChatGPT-4可直接在iPhone上使用啦

今天凌晨&#xff0c;OpenAI 正式在 App Store 推出了 ChatGPT 的 iOS app&#xff0c;瞬间冲上苹果商店免费榜第二名&#xff0c;效率榜第一名。 于是兴致勃勃的去下载体验了一番。整体不错&#xff0c;以后手机使用官方的 ChatGPT 更方便啦&#xff01;而且使用 GPT4 不再麻…

JavaScript事件流

一、事件流和它的两个阶段 1.事件流&#xff1a;是事件完整执行过程中的流动路径 2.说明&#xff1a;假设页面里有个div&#xff0c;当触发事件时&#xff0c;会经历两个阶段&#xff0c;分别是捕获阶段、冒泡阶段 &#xff08;1&#xff09;捕获&#xff1a;从父到子 &#…

测试工程师都是怎么写测试用例的?​

很多人不知道写测试用例有什么用&#xff0c;而仅仅是像工具人一样&#xff0c;在每次提测之前&#xff0c;把测试用例照着需求文档抄一遍&#xff0c;仿佛像是走个过场。 开发提测之后&#xff0c;就照着测试用例点点点&#xff0c;可能一天就走完用例了&#xff0c;开发代码…

最优化理论-线性规划中的大M法的步骤

目录&#xff1a; 一、引言 二、线性规划的基本概念 三、最优化理论中的大M法 1. 大M法的基本思想 2. 大M法的步骤 3. 大M法的优缺点 四、大M法的应用 1. 生产计划问题 2. 运输问题 3. 投资问题 五、总结 一、引言 最优化理论是数学中的一个重要分支…

【2023/05/19】NFA

Hello&#xff01;大家好&#xff0c;我是霜淮子&#xff0c;2023倒计时第14天。 非确定有限状态自动机&#xff08;NFA&#xff09;是一种模拟复杂系统行为的数学模型 目录 一、基本概念和理论 二、优点和缺点 三、应用场景 四、问题和挑战 五、重要性、作用和使用价值 …

学习HCIP的day.07

目录 7、SPF算法 --- OSPF防环机制 OSPF区域间防环 OSPF域外防环 基于以上长篇理论总结&#xff1a; 7、SPF算法 --- OSPF防环机制 &#xff08;1&#xff09;在同一个区域每台路由具有一致的LSDB &#xff08;2&#xff09;每台路由器以自己为根计算到达每个目标的最短路…

Java泛型,数组和方法返回类型 - 协变,逆变和不变

首先&#xff0c;让我们通常理解一下子类型规则是什么。 协变vs逆变vs双变vs不变 编程语言可能有支持以下子类型规则的特性&#xff1a; 协变 允许用超类型替换子类型。 逆变 允许用子类型替换超类型。 双变 同时是协变和逆变。 不变 不允许上述任何替换。 让我们看看Java支持哪…

Intellij IDEA 如何删掉插件

在 Intellij IDEA 的配置中&#xff0c;找到插件选项。 在插件选项中&#xff0c;选择需要删除的插件&#xff0c;然后在右侧的对话框中选择 uninstall 就可以了。 卸载以后&#xff0c;可能不会要求重启&#xff0c;为了安全起见&#xff0c;还是重启下你的 IDE 吧。

C++容器详解

什么是容器 首先&#xff0c;我们必须理解一下什么是容器&#xff0c;在C 中容器被定义为&#xff1a;在数据存储上&#xff0c;有一种对象类型&#xff0c;它可以持有其它对象或指向其它对像的指针&#xff0c;这种对象类型就叫做容器。很简单&#xff0c;容器就是保存其它对…

Flutter控件之文本Text封装

Flutter控件之基类Widget封装 上篇文章&#xff0c;我们简单针对Widget做了一个基类封装&#xff0c;拓展出了很多常见又易用的属性&#xff0c;比如宽高&#xff0c;内外边距等等&#xff0c;很方便的为接下来的各个基础组件的封装&#xff0c;提供极大的便利&#xff0c;在上…

虚拟机启动时出现“已启用侧通道缓解”的解决方法

系列文章目录 Hypervisor launch failed centos7配置ssh免密登陆完成&#xff0c;进行ssh登陆时出现”代理承认未能使用密钥签名“ 解决pip更新的代码 文章目录 系列文章目录 一、问题描述 二、启用了侧通道缓解的虚拟机可能会出现性能下降 &#xff08;79832&#xff0…

Linux系统vim查看文件中文乱码

Linux系统查看文件-cat中文正常显示 vim中文乱码 1、背景2、环境3、目的4、原因5、操作步骤5.1、修改vim编码配置 6、验证 1、背景 服务器部署业务过程中查看文件内容&#xff0c;使用cat 命令查看中文正常显示&#xff0c;使用vim命令查看显示中文乱码 cat 查看 vim 查看 …

陶哲轩宣布主持白宫生成式AI工作组,李飞飞、Hassabis发表演讲

来源 | 新智源 ID | AI-era 【导读】最近&#xff0c;「数学天才」陶哲轩表示&#xff0c;自己将领导白宫生成式人工智能工作组&#xff0c;就当前AI评估并收集意见。在陶哲轩看来&#xff0c;加入工作流的ChatGPT在数学专业领域中&#xff0c;并没有太多增值。 近来&#xf…

Redis主从复制、哨兵、cluster集群原理+实验

Redis 主从复制 主从复制&#xff0c;是指将一台Redis服务器的数据&#xff0c;复制到其他的Redis服务器。前者称为主节点(Master)&#xff0c;后者称为从节点(Slave)&#xff1b;数据的复制是单向的&#xff0c;只能由主节点到从节点。 默认情况下&#xff0c;每台Redis服务…

Fluent局部坐标系(曲线坐标系)

1 概述 在某些模型中&#xff0c;利用局部坐标系可极大的方便模型设置&#xff0c;例如对弯曲的多孔板设置多孔介质属性、设置各向异性的材料属性等。 2 创建坐标系 通过树状菜单中“curvilinear coordinate system”可创建曲线型局部坐标系。 右键点击“新建”&#xff0c;在如…

Linux 安装redis

一、概述 官网&#xff1a;https://redis.io/ Redis 是完全开源免费的&#xff0c;遵守BSD协议&#xff0c;是一个高性能的key-value数据库。 Redis 与其他 key - value 缓存产品有以下三个特点&#xff1a; Redis支持数据的持久化&#xff0c;可以将内存中的数据保持在磁盘…

基于静态和动态特征融合的语音情感识别层次网络

题目Hierarchical Network based on the Fusion of Static and Dynamic Features for Speech Emotion Recognition时间2021年期刊\会议ICASSP 基于静态和动态特征融合的语音情感识别层次网络 摘要&#xff1a;许多关于自动语音情感识别&#xff08;SER&#xff09;的研究都致…

【集群划分】基于kmeans的电压调节的集群划分【IEEE33节点】

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

HTTP/HTTPS协议详解

目录 一. HTTP详解 ✅1.1 概念 ✅1.2 HTTP的协议格式 1.2.1 HTTP请求体格式&#xff1a; 1.2.2 HTTP响应体格式&#xff1a; ✅1.3 HTTP请求方法 ✅1.4 认识请求报头 ✅1.5 HTTP请求过程 ✅1.6 认识状态码 二. HTTPS详解 ✅2.1 HTTPS简介 ✅2.2 HTTPS加密过程 TCP/UDP是位于传…

d3d(Direct X)中的com技术详解

本文不会对Com进行非常详细的分析 因为这个技术分析起来难度还是非常大的 要想真正弄懂还是非常困难的 我只会针对d3d中使用到的com技术和comptr技术进行说明 所以看完本文后 可以熟练使用d3d中使用到的相应技术 comptr类似于c11中的智能指针,用于管理com对象的生命周期,所以我…