栈Stack和队列Queue

目录

一、栈

(1)用数组实现

(2)用单链表实现

(3)用标注尾结点的单链表实现

(4)用双向链表实现

2、栈的实际应用

(1)改变元素的序列

(2)将递归转化为循环(逆序打印链表)

(3)括号匹配

(4)逆波兰表达式求值

(5)出栈入栈次序匹配

(6)最小栈

二、队列

1、队列的使用

 2、队列的模拟实现

3、循环队列

4、双端队列Deque

 三、面试题

1、用栈实现队列

2、用队列实现栈


一、栈

栈是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作

进行数据插入和删除操作的一端称为栈顶,另一端称为栈底

栈中的数据元素遵守后进先出的原则

  • 压栈/进栈/入栈:栈的插入操作
  • 出栈:栈的删除操作
方法
功能
Stack()构造一个空的栈
E push(E e)e入栈,并返回e
E pop()将栈顶元素出栈并返回
E peek()获取栈顶元素
int size()获取栈中有效元素个数
boolean empty()检测栈是否为空

1、栈的模拟实现

(1)用数组实现
(2)用单链表实现

  • 若使用尾插法,则入栈时间复杂度为O(n),出栈时间复杂度为O(n)
  • 若使用头插法,则入栈复杂度为O(1),出栈复杂度为O(1)
(3)用标注尾结点的单链表实现

  • 尾插法:入栈O(1),出栈O(n)(因为删除尾结点依旧要遍历到前一个结点,改变其next值)
  • 头插法:入栈O(1),出栈O(1)
(4)用双向链表实现

无论尾插还是头插时间复杂度都为O(1)

 LinkedList<Integer> stack=new LinkedList<>();
        stack.push(1);// addFirst(e);
        stack.push(2);
        stack.push(3);
        System.out.println(stack.peek());//拿到头结点3
2、栈的实际应用
(1)改变元素的序列
(2)将递归转化为循环(逆序打印链表)
 //1、递归方式
    void reverseprintList1(Node head){
        if(head!=null){
            printList(head.next);
            System.out.print(head.val + " ");
        }
    }
//2、循环方式
    void reverseprintList2(Node head){
        if(head==null){
            return;
        }
        Stack<Node> s = new Stack<>();
        // 将链表中的结点保存在栈中
        Node cur = head;
        while(cur!=null){
            s.push(cur);
            cur = cur.next;
        }
        // 将栈中的元素出栈
        while(!s.empty()){
            System.out.print(s.pop().val + " ");
        }
    }
(3)括号匹配

        给定一个只包含' ( '、' ) '、' { '、' } '、' [ '、' ] '的字符串s,判断括号是否匹配。匹配条件:左括号必须与相同类型的右括号闭合,不可交错排列

        匹配示例:(){}[]、({}[])、([{}])、()[{}]

public boolean isValid(String s) {
        Stack<Character> stack=new Stack<Character>();
        for (int i = 0; i < s.length(); i++) {
            char c=s.charAt(i);
            if (c=='(' || c=='[' || c=='{'){
                //c为左括号就添加进来等着对称匹配
                stack.push(c);
            }else {
                //c为右括号
                //此时栈中可能有左括号等着匹配,也可能为空(无法匹配)
                if (stack.empty()){
                    return false;
                }
                //不为空,则栈中存放的是左括号,那就判断c与栈顶元素是否匹配(因为两括号必须要对称匹配)
                char top=stack.peek();
                if (c==')' && top=='(' || c==']' && top=='[' || c=='}' && top=='{'){
                    //对称匹配了则弹出此左括号
                    stack.pop();
                }else {
                    //遇到一个右括号无法与栈顶左括号对称匹配,则无法匹配
                    return false;
                }
            }
        }
        //如果s遍历完发现栈中不为空,即栈中仍有残存左括号未进行匹配,不匹配
        if (!stack.empty()){
            return false;
        }
        //否则匹配
        return true;
    }
(4)逆波兰表达式求值

中缀表达式转化为后缀表达式技巧:

public int evalRPN(String[] tokens) {
        Stack<Integer> stack=new Stack<>();
        for (String s : tokens) {
            if (!isOperation(s)) {
                //压入数字字符
                stack.push(Integer.parseInt(s));
            }else {
                //运算符则弹出栈顶2个元素进行操作(先弹出的放运算符右边)
                int num2=stack.pop();
                int num1=stack.pop();
                switch (s){
                    case "+":
                        stack.push(num1+num2);
                        break;
                    case "-":
                        stack.push(num1-num2);
                        break;
                    case "*":
                        stack.push(num1*num2);
                        break;
                    case "/":
                        stack.push(num1/num2);
                        break;
                    
                }
            }
        }
        return stack.pop();
    }

    private boolean isOperation(String s) {
        if (s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/")){
            //是运算符
            return true;
        }
        return false;
    }
(5)出栈入栈次序匹配

 public boolean IsPopOrder (int[] pushV, int[] popV) {
        Stack<Integer> stack=new Stack<>();
        int j=0;
        for (int i = 0; i < pushV.length; i++) {
            stack.push(pushV[i]);
            while (!stack.empty() && j<popV.length && stack.peek()==popV[j]){
                stack.pop();
                j++;
            }
        }
        return stack.empty();
    }
(6)最小栈
class MinStack {
    private Stack<Integer> stack;
    private Stack<Integer> minStack;//存储阶段性最小值
    private int minValue;

    public MinStack() {
        stack=new Stack<>();
        minStack=new Stack<>();
    }

    public void push(int val) {
        stack.push(val);
        if (minStack.empty()){
            minStack.push(val);
        }else {
            if (val<=minStack.peek()){
                minStack.push(val);
            }
        }
    }

    public void pop() {
        if (!stack.empty()){
            int ret=stack.pop();
            if (minStack.peek()==ret){
                minStack.pop();
            }
        }
    }

    public int top() {
        //获取stack栈顶元素
        if (stack.empty()){
            return -1;
        }
        return stack.peek();
    }

    public int getMin() {
        //获取minStack栈顶元素
        if (minStack.empty()){
            return -1;
        }
        return minStack.peek();
    }
}
  • :一种数据结构。是一种特殊的线性表,只允许在栈顶进行插入和删除操作,栈顶的数据元素遵守后进先出的原则
  • 虚拟机栈:JVM内存管理的一部分,用于管理函数调用的内存和回收。属于线程私有,确保每个线程的内存隔离和安全。
  • 栈帧:函数调用过程中的内存管理单元。包含局部变量表、操作栈等信息。每个方法在运行时JVM都会创建一个栈帧,并将其压入虚拟机栈中;当方法调用结束时对应的栈帧会从虚拟机栈中出栈,确保函数调用的顺利进行和结束后的资源释放

二、队列

队列:只允许在一端进行插入和删除数据操作,在另一端进行删除数据操作的特殊线性表

  • 进行插入操作的一端称为队尾,进行删除操作的一端称为队头
  • 队列遵守先进先出的原则
1、队列的使用

在Java中,Queue是个接口,底层是通过链表实现的

方法
功能
boolean offer(E e)入队列
E poll()出队列
peek()获取队头元素
int size()获取队列中有效元素个数
boolean isEmpty()检测队列是否为空

Queue是个接口,在实例化时必须实例化LInkedList的对象,因为LinkedList实现了Queue接口

        Queue<Integer> q = new LinkedList<>();
        // 从队尾入队列
        q.offer(1);
        q.offer(2);
        q.offer(3);
        q.offer(4);
        q.offer(5);
        System.out.println(q.size());//5
        System.out.println(q.peek()); // 获取队头元素 1
        q.poll();//弹出队头元素 1
        System.out.println(q.poll()); // 从队头出队列,并将删除的元素返回 2
        if(q.isEmpty()){
            System.out.println("队列空");
        }else{
            System.out.println(q.size());//3
        }
 2、队列的模拟实现

队列中既然可以存储元素,那底层肯定要有能够保存元素的空间。通过前面线性表的学习了解到常见的空间类型有2种:顺序结构和链式结构。那是用哪种结构实现比较好呢?

(1)双向链表实现

(2)数组实现

如果rear==elem.length-1之后发现数组前面还有位置还可以往前面插入元素就好了,这时候也就是我们的循环队列

3、循环队列

数组设计循环队列 

4、双端队列Deque

双端队列是指允许两端都可以进行入队和出队操作的队列

Deque是一个接口,使用时必须创建LinkedList对象

在实际工程中,使用Deque接口是比较多的,栈和队列均可以使用该接口

Deque<Integer>stack =newArrayDeque<>();//双端队列的线性实现
Deque<Integer>queue=newLinkedList<>();//双端队列的链式实现

 三、面试题

1、用栈实现队列

class MyQueue {
    Stack<Integer> inStack;
    Stack<Integer> outStack;

    public MyQueue() {
        inStack=new Stack<>();
        outStack=new Stack<>();
    }

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

    public int pop() {
        if (empty()){
            return -1;//队列此时为空
        }
        if (outStack.empty()){
            while (!inStack.empty()){
                outStack.push(inStack.pop());
            }
        }
        return outStack.pop();
    }

    public int peek() {
        if (empty()){
            return -1;
        }
        if (outStack.empty()){
            while (!inStack.empty()){
                outStack.push(inStack.pop());
            }
        }
        return outStack.peek();
    }

    public boolean empty() {
        return inStack.empty() && outStack.empty();
    }
}
2、用队列实现栈

class MyStack {
    private Queue<Integer> qu1;
    private Queue<Integer> qu2;


    public MyStack() {
        qu1=new LinkedList<>();
        qu2=new LinkedList<>();
    }

    //入栈入到不为空的队列,都为空则入到qu1即可
    public void push(int x) {
        if (!qu1.isEmpty()){
            qu1.offer(x);
        }else if (!qu2.isEmpty()){
            qu2.offer(x);
        }else {
            qu1.offer(x);
        }
    }

    //出栈出不为空的队列size-1个。最后一个元素就是要出栈的元素
    public int pop() {
        if (empty()){
            return -1;//栈为空
        }
        if (!qu1.isEmpty()){
            int size=qu1.size();
            for (int i = 0; i < size - 1; i++) {
                qu2.offer(qu1.poll());
            }
            return qu1.poll();
        }else {
            int size=qu2.size();
            for (int i = 0; i < size - 1; i++) {
                qu1.offer(qu2.poll());
            }
            return qu2.poll();
        }
    }

    public int top() {
        int tmp=-1;
        if (empty()){
            return -1;//栈为空
        }
        if (!qu1.isEmpty()){
            int size=qu1.size();
            for (int i = 0; i < size; i++) {
                tmp=qu1.poll();
                qu2.offer(tmp);//出的最后一个元素存在tmp即为栈顶元素
            }
            return tmp;
        }else {
            int size=qu2.size();
            for (int i = 0; i < size; i++) {
                tmp=qu2.poll();
                qu1.offer(tmp);
            }
            return tmp;
        }
    }

    public boolean empty() {
        return qu2.isEmpty() && qu1.isEmpty();
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ES6标准-Promise对象

目录 Promise对象的含义 Promise对象的特点 Promise对象的缺点 Promise对象的基本用法 Promise对象的简单例子 Promise新建后就会立即执行 Promise对象回调函数的参数 Promise参数不会中断运行 Promise对象的then方法 Promise对象的catch()方法 Promise状态为resolv…

【隐私计算】隐私计算的应用场景探索(大模型隐私计算、隐私数据存储计算、Web3、隐私物联网等)

1. 背景分析 隐私计算作为一种实现“原始数据不出域&#xff0c;可用不可见”的数据流通价值的关键技术&#xff0c;经历了2020-2023年的高光时刻&#xff0c;却在2024年骤然走向低谷。从各种渠道了解到一些业内曾经风光无两的隐私计算公司都有不同程度的裁员。几乎一夜之间&am…

【大数据学习 | flume】flume的概述与组件的介绍

1. flume概述 Flume是cloudera(CDH版本的hadoop) 开发的一个分布式、可靠、高可用的海量日志收集系统。它将各个服务器中的数据收集起来并送到指定的地方去&#xff0c;比如说送到HDFS、Hbase&#xff0c;简单来说flume就是收集日志的。 Flume两个版本区别&#xff1a; ​ 1&…

【大语言模型】ACL2024论文-16 基于地图制图的罗马尼亚自然语言推理语料库的新型课程学习方法

【大语言模型】ACL2024论文-16 基于地图制图的罗马尼亚自然语言推理语料库的新型课程学习方法 目录 文章目录 【大语言模型】ACL2024论文-16 基于地图制图的罗马尼亚自然语言推理语料库的新型课程学习方法目录摘要&#xff1a;研究背景&#xff1a;问题与挑战&#xff1a;如何解…

数据库审计工具--Yearning 3.1.9普民的使用指南

1 页面登录 登录地址:18000 &#xff08;不要勾选LDAP&#xff09; 2 修改用户密码 3 DML/DDL工单申请及审批 工单申请 根据需要选择【DML/DDL/查询】中的一种进行工单申请 填写工单信息提交SQL检测报错修改sql语句重新进行SQL检测&#xff0c;如检测失败可以进行SQL美化后…

Day44 | 动态规划 :状态机DP 买卖股票的最佳时机IV买卖股票的最佳时机III

Day44 | 动态规划 &#xff1a;状态机DP 买卖股票的最佳时机IV&&买卖股票的最佳时机III&&309.买卖股票的最佳时机含冷冻期 动态规划应该如何学习&#xff1f;-CSDN博客 本次题解参考自灵神的做法&#xff0c;大家也多多支持灵神的题解 买卖股票的最佳时机【…

Windows配置域名映射IP

一、找到 hosts 文件 打开 C:\Windows\System32\drivers\etc 二、添加hosts文件修改、写入权限 右击hosts文件&#xff0c;点击属性 -> 安全 -> Users -> 编辑 -> Users -> 添加修改、写入权限 -> 确定 -> 确定 进入常规&#xff0c;将只读属性关闭 三、…

sapiens推理的安装与使用

文章目录 1、安装1.1 克隆代码库1.2 设置 Sapiens-Lite 的代码路径1.3 创建 Conda 环境并安装必要的依赖1.4 下载模型检查点 2、推理 sapiens&#xff0c;是meta发布的以人为中心的视觉大模型&#xff0c;"sapiens"这个词来源于拉丁语&#xff0c;意为“智慧的”或“…

黑马智数Day10

项目背景说明 后台管理部分使用的技术栈是Vue2&#xff0c;前台可视化部分使用的技术栈是Vue3 前台可视化项目不是独立存在&#xff0c;而是和后台管理项目共享同一个登录页面 微前端的好处 微前端是一种前端架构模式&#xff0c;它将大型单体应用程序分解为小的、松散耦合的…

A3超级计算机虚拟机,为大型语言模型LLM和AIGC提供强大算力支持

热门大语言模型项目地址&#xff1a;www.suanjiayun.com/mirrorDetails?id66ac7d478099315577961758 近几个月来&#xff0c;我们目睹了大型语言模型&#xff08;LLMs&#xff09;和生成式人工智能强势闯入我们的视野&#xff0c;显然&#xff0c;这些模型在训练和运行时需要…

乐维网管平台(七):网络稳定与高效的“安全锦囊”

试想一下&#xff0c;你给电脑升级了一个软件&#xff0c;升级完成后发现有BUG&#xff0c;经常无故卡死&#xff0c;这时候想回退或重新安装旧版本…相对地&#xff0c;一家企业的网络管理员&#xff0c;在对公司的核心交换机进行复杂的配置调整时&#xff0c;一个小小的疏忽&…

基于Python的图片信息推荐系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

没钱买KEGG怎么办?REACTOME开源通路更强大

之前搜集免费生物AI插图时简单提到了通路数据库Reactome&#xff08;https://reactome.org/&#xff09;&#xff0c; 那些精美的生物插图只能算是该数据库附赠的小礼品&#xff0c;他的主要功能还是作为一个开源的通路数据库&#xff0c;为相关领域的研究者提供直观的可视化生…

spi 回环

///tx 极性0 &#xff08;sclk信号线空闲时为低电平&#xff09; /// 相位0 (在sclk信号线第一个跳变沿进行采样) timescale 1ns / 1ps//两个从机 8d01 8d02 module top(input clk ,input rst_n,input [7:0] addr ,input …

Lc70--319.两个数组的交集(二分查找)---Java版

1.题目描述 2.思路 用集合求交集&#xff0c;因为集合里面的元素要满足不重复、无序、唯一。使得集合在去重、查找和集合操作&#xff08;如交集、并集、差集等&#xff09;中非常高效和方便。 3.代码实现 class Solution {public int[] intersection(int[] nums1, int[] nu…

项目2:简易随机数生成器 --- 《跟着小王学Python·新手》

项目2&#xff1a;简易随机数生成器 — 《跟着小王学Python新手》 《跟着小王学Python》 是一套精心设计的Python学习教程&#xff0c;适合各个层次的学习者。本教程从基础语法入手&#xff0c;逐步深入到高级应用&#xff0c;以实例驱动的方式&#xff0c;帮助学习者逐步掌握P…

qml绘制折线图

参考链接 qml绘制折线图 在QML&#xff08;Qt Modeling Language&#xff09;中绘制折线图可以通过使用Canvas元素或ChartView元素来实现。以下是两种方法的示例&#xff1a; 方法一&#xff1a;使用Canvas元素 Canvas元素允许你在QML中绘制自定义图形。你可以通过JavaScrip…

MODBUS TCP转CANOpen网关

Modbus TCP转CANopen网关 型号&#xff1a;SG-TCP-COE-210 产品用途 本网关可以实现将CANOpen接口设备连接到MODBUS TCP网络中&#xff1b;并且用户不需要了解具体的CANOpen和Modbus TCP 协议即可实现将CANOpen设备挂载到MODBUS TCP接口的 PLC上&#xff0c;并和CANOpen设备…

Spring Cloud Alibaba [Gateway]网关。

1 简介 网关作为流量的入口&#xff0c;常用功能包括路由转发、权限校验、限流控制等。而springcloudgateway 作为SpringCloud 官方推出的第二代网关框架&#xff0c;取代了Zuul网关。 1.1 SpringCloudGateway特点: &#xff08;1&#xff09;基于Spring5&#xff0c;支持响应…

delphi fmx android 离线人脸识别

搜遍全网都没有找到delphi android 能用的 离线人脸识别,无需注册什么开发者 有这方面需求的可以用fsdk 这边用的luxand.FSDK8.0 android下的注册号要自己找下 1,用老猫的工具将android 下的sdk,FSDK.java 编译成FSDK.jar 老猫的工具 2,用上面的工具将FSDK.jar 生成de…