【数据结构】第五弹——Stack 和 Queue

文章目录

  • 一. 栈(Stack)
    • 1.1 概念
    • 1.2 栈的使用
    • 1.3 栈的模拟实现
      • 1.3.1 顺序表结构
      • 1.3.2 进栈 压栈
      • 1.3.3 删除栈顶元素
      • 1.3.4 获取栈顶元素
      • 1.3.5 自定义异常
    • 1.4 栈的应用场景
      • 1.改变元素序列
      • 2. 将递归转化为循环
      • 3. 四道习题
    • 1.5 概念分区
  • 二. 队列(Queue)
    • 2.1 概念
    • 2.2 队列的使用
    • 2.3 队列模拟实现
    • 2.4 循环队列
      • 数组下标循环的小技巧
      • 设计循环队列
  • 三. 双端队列 (Deque)
  • 四. 两道面试题
    • 4.1 用队列实现栈
    • 4.2 用栈实现队列

一. 栈(Stack)

1.1 概念

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
在这里插入图片描述

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据在栈顶。

1.2 栈的使用

在这里插入图片描述

public static void main(String[] args) {Stack<Integer> s = new Stack();s.push(1);s.push(2);s.push(3);s.push(4);System.out.println(s.size());   // 获取栈中有效元素个数---> 4System.out.println(s.peek());   // 获取栈顶元素---> 4s.pop();   // 4出栈,栈中剩余1   2   3,栈顶元素为3System.out.println(s.pop());   // 3出栈,栈中剩余1  2   栈顶元素为3if(s.empty()){System.out.println("栈空");}else{System.out.println(s.size());}}

1.3 栈的模拟实现

在这里插入图片描述
Stack继承了Vector,Vector和ArrayList类似,都是动态的顺序表

1.3.1 顺序表结构

public class MyStack {public int[] elem;public int usedSize;public MyStack() {this.elem = new int[10];}
}

1.3.2 进栈 压栈

 public void push(int val) {if(isFull()) {this.elem = Arrays.copyOf(elem,2*elem.length);}elem[usedSize++] = val;}public boolean isFull() {return usedSize == elem.length;}

1.3.3 删除栈顶元素

public int pop() {if(isEmpty()) {throw new EmptyStackException();}int val = elem[usedSize-1];usedSize--;return val;}

1.3.4 获取栈顶元素

 public int peek() {if(isEmpty()) {throw new EmptyStackException();}return elem[usedSize-1];}public boolean isEmpty() {return usedSize == 0;}

1.3.5 自定义异常

public class EmptyStackException extends RuntimeException{public EmptyStackException() {}public EmptyStackException(String message) {super(message);}
}

完整代码:

public class MyStack {public int[] elem;public int usedSize;public MyStack() {this.elem = new int[10];}public void push(int val) {if(isFull()) {this.elem = Arrays.copyOf(elem,2*elem.length);}elem[usedSize++] = val;}public boolean isFull() {return usedSize == elem.length;}public int pop() {if(isEmpty()) {throw new EmptyStackException();}int val = elem[usedSize-1];usedSize--;return val;}//获取栈顶元素 但是不删除public int peek() {if(isEmpty()) {throw new EmptyStackException();}return elem[usedSize-1];}public boolean isEmpty() {return usedSize == 0;}
}

1.4 栈的应用场景

1.改变元素序列

在这里插入图片描述

2. 将递归转化为循环

逆序打印链表

// 递归方式
void printList(Node head){if(null != head){printList(head.next);System.out.print(head.val + " ");}
}// 循环方式
void printList(Node head){if(null == head){return;}Stack<Node> s = new Stack<>();// 将链表中的结点保存在栈中Node cur = head;while(null != cur){s.push(cur);cur = cur.next;}// 将栈中的元素出栈while(!s.empty()){System.out.print(s.pop().val + " ");}
}

3. 四道习题

在这里插入图片描述

都是力扣牛客网上的题目,也是常见的面试题,会在刷题专栏详细讲解

1.5 概念分区

栈 虚拟机栈 栈帧有什么区别?

  • 栈是一种通用的数据结构概念,而虚拟机栈是在 Java 虚拟机环境下对栈这种数据结构的具体应用
  • 虚拟机栈由多个栈帧组成,每个栈帧对应一个方法的调用。当方法调用开始时,会创建一个新的栈帧并压入虚拟机栈;当方法调用结束时,对应的栈帧会从虚拟机栈中弹出
  • 综上所述,栈是一种抽象的数据结构,虚拟机栈是 JVM 中使用栈结构来管理方法调用的具体实现,而栈帧则是虚拟机栈中用于支持单个方法执行的具体数据结构

二. 队列(Queue)

2.1 概念

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾(Tail/Rear) 出队列:进行删除操作的一段称为队头(Head/Front)
在这里插入图片描述

2.2 队列的使用

在Java中,Queue是个接口,底层是通过链表实现的
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

每个方法都有一个类似的方法
从效果上没有区别
在这里插入图片描述

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

在这里插入图片描述

在这里插入图片描述

LinkedList实现的方法是很多的,可以直接拿来用
那就要这样写了:

LinkedList <Integer> q = new LinkedList<>();

2.3 队列模拟实现

队列中既然可以存储元素,那底层肯定要有能够保存元素的空间,通过前面线性表的学习了解到常见的空间类型有两种:顺序结构 和 链式结构

队列的实现使用顺序结构还是链式结构好?

我们先说链式结构:
在这里插入图片描述
在这里插入图片描述
双向链表实现队列

 static class ListNode{public int val;public ListNode prev;public ListNode next;public ListNode(int val){this.val=val;}}public ListNode first=null;public ListNode last=null;public int useSize=0;public void offer(int val){//addLast尾插ListNode node=new ListNode(val);if(isEmpty()){first=last=null;}last.next=node;node.prev=last;last=last.next;useSize++;}public int poll(){//头删if(isEmpty()){return -1;}int val= first.val;//队头的数据first=first.next;if(first!=null){//只剩一个节点 直接useSize--first.prev=null;}useSize--;return 0;}public int peek(){//获取队头元素if(first==null){return -1;}return first.val;}public boolean isEmpty(){return useSize==0;}

双向链表实现队列效率很高 删除 插入都很方便

如果是数组实现队列该如何实现?

其实按照顺序遍历插入删除 可以实现,但是数组的遍历 一旦往后走就无法回去 被删除的空间没办法再次使用了

如果数组是一个环就好了,这样就可以走回去再次使用空间–这就是循环队列

2.4 循环队列

我们有时还会使用一种队列叫循环队列如操作系统课程讲解生产者消费者模型时可能就会使用循环队列。环形队列通常使用数组实现。

在这里插入图片描述
在这里插入图片描述

上面两个一个是空的环形队列 一个是满的环形队列

实现环形队列两个问题:
1.怎么判断空和满?
2.rear 和 front 下标从7到0怎么做到的?

解答:
空:只要front 和 rear 相遇就是空的
满 有三种方法判断
1.定义size size=数组长度就是满
2.添加标记 boolean类型元素 一开始是false一旦开始添加元素就变为true
3.浪费一个空间 判断rear是不是front

先放入元素 再rear++ 判断rear的下一个元素是不是front就可以了
在这里插入图片描述

判断 rear=(rear+1)%len

关于这个公式:

数组下标循环的小技巧

在这里插入图片描述
在这里插入图片描述
利用取模运算得到下标

好了,解决了这两个问题,我们可以使用循环队列了,正好我们来看一道设计循环队列的题目

设计循环队列

设计循环队列

class MyCircularQueue {//数组实现public int front;public int rear;public int[] elem;public MyCircularQueue(int k) {//使用浪费一个空间的方法来判断队列是否满//数组多给一个空间elem=new int[k+1];}public boolean enQueue(int value) {//插入 判满if(isFull()){return false;}elem[rear]=value;rear =(rear+1)%elem.length;return true;}public boolean deQueue() {//删除 判空if(isEmpty()){return false;}//头向后走 因为是环形的所以 向后走也不用担心前面的空间不能使用了front=(front+1)%elem.length;return true;}public int Front() {if(isEmpty()){return -1;}return elem[front];}public int Rear() {if(isEmpty()){return-1;}//return elem[rear];}public boolean isEmpty() {return rear==front;}public boolean isFull() {//rear 的下一个是 frontreturn (rear+1)%elem.length==front;}
}/*** Your MyCircularQueue object will be instantiated and called as such:* MyCircularQueue obj = new MyCircularQueue(k);* boolean param_1 = obj.enQueue(value);* boolean param_2 = obj.deQueue();* int param_3 = obj.Front();* int param_4 = obj.Rear();* boolean param_5 = obj.isEmpty();* boolean param_6 = obj.isFull();*/

如果是这样写示例测到这里会有一个报错
原因在于我们并没有考虑 最重要的一点
rear 从尾到头 怎么办 front删除是向后走了 rear等于0 是可以的
rear 的下一个确实不是front 可以等于0
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

正确代码如下:

class MyCircularQueue {//数组实现public int front;public int rear;public int[] elem;public MyCircularQueue(int k) {//使用浪费一个空间的方法来判断队列是否满//数组多给一个空间elem=new int[k+1];}public boolean enQueue(int value) {//插入 判满if(isFull()){return false;}elem[rear]=value;rear =(rear+1)%elem.length;return true;}public boolean deQueue() {//删除 判空if(isEmpty()){return false;}//头向后走 因为是环形的所以 向后走也不用担心前面的空间不能使用了front=(front+1)%elem.length;return true;}public int Front() {if(isEmpty()){return -1;}return elem[front];}public int Rear() {if(isEmpty()){return-1;}//int index=(rear==0)?elem.length-1:rear-1;return elem[index];}public boolean isEmpty() {return rear==front;}public boolean isFull() {//rear 的下一个是 frontreturn (rear+1)%elem.length==front;}
}

三. 双端队列 (Deque)

双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。那就说明元素可以从队头出队和入队,也可以从队尾出队和入队

在这里插入图片描述

Deque是一个接口,使用时必须创建LinkedList的对象
在实际工程中,使用Deque接口是比较多的,栈和队列均可以使用该接口

再来看一下我们数据结构第一篇文章画的图,想打关一样,已经学完这么多了

在这里插入图片描述
两种使用方式

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

四. 两道面试题

4.1 用队列实现栈

队列实现栈
在这里插入图片描述

class MyStack {public Queue<Integer> qu1;public Queue<Integer> qu2;public MyStack() {qu1=new LinkedList<>();qu2=new LinkedList<>();}//入栈public void push(int x) {if(!qu1.isEmpty()){qu1.offer(x);}else if(!qu2.isEmpty()){qu2.offer(x);}else{qu1.offer(x);}}//出栈public int pop() {if(empty()){return-1;}else if(!qu1.isEmpty()){ // 1不是空 将1的size-1个元素放到2for(int i=0;i<qu1.size()-1;i++){qu2.offer(qu1.poll());}return qu1.poll();}else{//2 不为空for(int i=0;i<qu2.size()-1;i++){qu1.offer(qu2.poll());}return qu2.poll();}}public int top() {if(empty()){return-1;}//用一个临时变量 把每个删除的都记录一下,最后一个就是栈顶if(!qu1.isEmpty()){ // 1不是空 将1的size-1个元素放到2int val=0;for(int i=0;i<qu1.size();i++){val=qu1.poll();qu2.offer(val);}return val;}else{//2 不为空int val=0;for(int i=0;i<qu2.size();i++){val=qu2.poll();qu1.offer(val);}return val;}}public boolean empty() {return qu1.isEmpty()&&qu2.isEmpty();}
}

在这里插入图片描述
在这里插入图片描述
正确代码:

class MyStack {public Queue<Integer> qu1;public Queue<Integer> qu2;public MyStack() {qu1=new LinkedList<>();qu2=new LinkedList<>();}//入栈public void push(int x) {if(!qu1.isEmpty()){qu1.offer(x);}else if(!qu2.isEmpty()){qu2.offer(x);}else{qu1.offer(x);}}//出栈public int pop() {if(empty()){return-1;}else if(!qu1.isEmpty()){ // 1不是空 将1的size-1个元素放到2int size=qu1.size();for(int i=0;i<size-1;i++){qu2.offer(qu1.poll());}return qu1.poll();}else{//2 不为空int size=qu2.size();for(int i=0;i<size-1;i++){qu1.offer(qu2.poll());}return qu2.poll();}}public int top() {if(empty()){return-1;}//用一个临时变量 把每个删除的都记录一下,最后一个就是栈顶if(!qu1.isEmpty()){ // 1不是空 将1的size-1个元素放到2int size=qu1.size();int val=0;for(int i=0;i<size;i++){val=qu1.poll();qu2.offer(val);}return val;}else{//2 不为空int size=qu2.size();int val=0;for(int i=0;i<size;i++){val=qu2.poll();qu1.offer(val);}return val;}}public boolean empty() {return qu1.isEmpty()&&qu2.isEmpty();}
}

4.2 用栈实现队列

在这里插入图片描述
按照我们画图得到的思路,写代码:

class MyQueue {
// 两个栈
public ArrayDeque<Integer> s1;
public ArrayDeque<Integer> s2;public MyQueue() {s1=new ArrayDeque<>();s2=new ArrayDeque<>();}public void push(int x) {//入队s1.push(x);}public int pop() {if(empty()){return -1;}if(s2.isEmpty()){while(!s1.isEmpty()){s2.push(s1.pop());}}return s2.pop();}public int peek() {if(empty()){return -1;}if(s2.isEmpty()){while(!s1.isEmpty()){s2.push(s1.pop());}}return s2.peek();}public boolean empty() {return s1.isEmpty()&&s2.isEmpty();}
}

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

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

相关文章

第七届能源系统与电气电力国际学术会议(ICESEP 2025)

重要信息 时间&#xff1a;2025年6月20-22日 地点&#xff1a;中国-武汉 官网&#xff1a;www.icesep.net 主题 能源系统 节能技术、能源存储技术、可再生能源、热能与动力工程 、能源工程、可再生能源技术和系统、风力发…

深入解析C++ STL Stack:后进先出的数据结构

一、引言 在计算机科学中&#xff0c;栈&#xff08;Stack&#xff09;作为一种遵循后进先出&#xff08;LIFO&#xff09;​原则的数据结构&#xff0c;是算法设计和程序开发的基础构件。C STL中的stack容器适配器以简洁的接口封装了底层容器的操作&#xff0c;为开发者提供了…

Golang | 自行实现并发安全的Map

核心思路&#xff0c;读写map之前加锁&#xff01;哈希思路&#xff0c;大map化分为很多个小map

Mac 「brew」快速安装MySQL

安装MySQL 在 macOS 上安装 MySQL 环境可以通过Homebrew快速实现&#xff0c;以下是步骤指南&#xff1a; 方法 1&#xff1a;使用 Homebrew 安装 MySQL 1. 安装 Homebrew 如果尚未安装 Homebrew&#xff0c;可以通过以下命令安装&#xff1a; /bin/bash -c "$(curl -…

【数字孪生世界的搭建之旅:从0到1理解飞渡平台】

数字孪生世界的搭建之旅&#xff1a;从0到1理解飞渡平台 前言&#xff1a;数字分身的魔法 想象一下&#xff0c;如果你能在现实世界之外&#xff0c;创造一个物理世界的"分身"&#xff0c;这个分身能完美复制现实中的一切变化&#xff0c;甚至可以预测未来可能发生…

【漏洞复现】Struts2系列

【漏洞复现】Struts2系列 1. 了解Struts21. Struts2 S2-061 RCE &#xff08;CVE-2020-17530&#xff09;1. 漏洞描述2. 影响版本3. 复现过程 1. 了解Struts2 Apache Struts2是一个基于MVC设计模式的Web应用框架&#xff0c;会对某些标签属性&#xff08;比如 id&#xff09;的…

[FPGA Video IP] Video Processing Subsystem

Xilinx Video Processing Subsystem IP (PG231) 详细介绍 概述 Xilinx LogiCORE™ IP Video Processing Subsystem (VPSS)&#xff08;PG231&#xff09;是一个高度可配置的视频处理模块&#xff0c;设计用于在单一 IP 核中集成多种视频处理功能&#xff0c;包括缩放&#xf…

自动驾驶(ADAS)功能--相关名称及缩写

根据《道路车辆先进驾驶辅助系统&#xff08;ADAS&#xff09;术语及定义》GB/T 39263—2020&#xff0c;如下表格&#xff1a; 编号中文术语英文缩写定义类别2.1.1先进驾驶辅助系统ADAS利用传感、通信、决策及执行等装置&#xff0c;实时监测驾驶员、车辆及行驶环境&#xff…

1.9软考系统架构设计师:优秀架构设计师 - 超简记忆要点、知识体系全解、考点深度解析、真题训练附答案及解析

超简记忆要点 1. 优秀架构师标准 ✅ 技术&#xff08;深度/广度&#xff09; 实战&#xff08;大型项目&#xff09; 素养&#xff08;沟通/业务前瞻&#xff09; 2. 演化路径 &#x1f4c8; 积累&#xff08;技术/项目&#xff09; → 思维&#xff08;系统视角/抽象建模&…

(MySQL)库的操作

目录 创建数据库 语法 创建数据库实例 不使用可选项 使用可选项1 字符集和校验规则 校验规则对数据库的影响 不区分大小写 查看配置 添加可选项2 操纵数据库 使用数据库 查看数据库 查看所有数据库 查询当前正在使用的数据库名称 显示创建数据库语句 修改数据库…

10.ArkUI Grid的介绍和使用

ArkUI Grid 组件详解与使用指南 Grid 是 ArkUI 中用于实现网格布局的容器组件&#xff0c;能够以行和列的形式排列子组件。以下是 Grid 组件的详细介绍和使用方法。 基本介绍 Grid 组件特点&#xff1a; 支持固定列数和自适应布局提供灵活的间距和排列控制支持滚动显示大量…

目标检测原理简介

目标检测是一类计算机视觉任务,简单来说,目标检测可被定义为在计算机中输入一张图像,计算机需要找出图像中所有感兴趣的目标(物体),确定它们的类别和位置,如图一所示。目标检测是计算机视觉领域的核心问题之一,相较于最原始的将整张图片分类为某一类别,目标检测不光可…

ZYNQ笔记(十四):基于 BRAM 的 PS、PL 数据交互

版本&#xff1a;Vivado2020.2&#xff08;Vitis&#xff09; 实验任务&#xff1a; PS 将字符串数据写入BRAM&#xff0c;再将数据读取出来&#xff1b;PL 从 BRAM 中读取数据&#xff0c;bing。通过 ILA 来观察读出的数据&#xff0c;与前面串口打印的数据进行对照&#xff0…

Python-Django系列—部件

部件是 Django 对 HTML 输入元素的表示。部件处理 HTML 的渲染&#xff0c;以及从对应于部件的 GET&#xff0f;POST 字典中提取数据。 内置部件生成的 HTML 使用 HTML5 语法&#xff0c;目标是 <!DOCTYPE html>。例如&#xff0c;它使用布尔属性&#xff0c;如 checked…

【Leetcode 每日一题】2799. 统计完全子数组的数目

问题背景 给你一个由 正 整数组成的数组 n u m s nums nums。 如果数组中的某个子数组满足下述条件&#xff0c;则称之为 完全子数组 &#xff1a; 子数组中 不同 元素的数目等于整个数组不同元素的数目。 返回数组中 完全子数组 的数目。 子数组 是数组中的一个连续非空序…

卷积神经网络(二)

1 卷积运算的两个问题&#xff1a; 1.1 图像边缘信息使用少 边缘的像素点可能只会被用一次或者2次&#xff0c;中间的会用的更多。 1.2 图像被压缩 5*5的图像&#xff0c;如果经过3*3的卷积核后&#xff0c;大小变成3*3的。 N*N的图像&#xff0c;果经过F*F的卷积核后&#x…

组网技术-DHCP服务器,RIP协议,OSPF协议

1.DHCP Server提供三种IP地址分配策略&#xff1a; 手工分配地址 自动分配地址 n 动态分配地址 2.DHCP报文类型 DHCP DISCOVER(广播)&#xff1a;用于寻址DHCP Server DHCP OFFER&#xff08;单播&#xff09;&#xff1a;携带分配给客户端的IP地址 DHCP REQUEST&#xff08;…

反爬策略应对指南:淘宝 API 商品数据采集的 IP 代理与请求伪装技术

一、引言​ 在电商数据驱动决策的时代&#xff0c;淘宝平台海量的商品数据极具价值。然而&#xff0c;淘宝为保障平台安全和用户体验&#xff0c;构建了严密的反爬体系。当采集淘宝 API 商品数据时&#xff0c;若不采取有效措施&#xff0c;频繁的请求极易触发反爬机制&#x…

学习笔记(算法学习+Maven)

单调队列优化多重背包 #include <bits/stdc.h> using namespace std; const int M 2010; const int N 20010; int q[N]; int hh 0, tt -1; int f[N]; int g[N]; int v[M], w[M], s[M]; int n, m; int main() { cin >> n >> m; for (int i 1; …

WPF之项目创建

文章目录 引言先决条件创建 WPF 项目步骤理解项目结构XAML 与 C# 代码隐藏第一个 "Hello, WPF!" 示例构建和运行应用程序总结相关学习资源 引言 Windows Presentation Foundation (WPF) 是 Microsoft 用于构建具有丰富用户界面的 Windows 桌面应用程序的现代框架。它…