数据结构初阶(栈和队列)
文章目录
- 一、栈
- 1.1 什么是栈
- 1.2 栈的使用
- (1)底层代码
- (2)方法
- (3)栈的应用
- 二、队列
- 2.1 什么是队列
- 2.2 队列的使用
- (1)底层代码的实现
- (2)队列的使用
- 2.3 双端队列
- 2.4 练习
一、栈
1.1 什么是栈
- 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。
- 栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则
- 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
- 出栈:栈的删除操作叫做出栈。出数据在栈顶。
- JVM虚拟机栈 VS 栈
- JVM虚拟机栈:系统的一块内存
- 栈:数据结构

1.2 栈的使用
(1)底层代码
import java.util.Arrays;public class MyStack {private int[] elem; //stack的底层是数组private int usedSize;public MyStack() {this.elem = new int[5];}//压栈public void push(int val) {if(isFull()) { //判断里面的空间是否为满的情况elem = Arrays.copyOf(elem,2*elem.length);}elem[usedSize] = val;usedSize++;}public boolean isFull() {return usedSize == elem.length;}//出栈public int pop() {//1、判断栈不为空if(empty()) {//抛出异常!!throw new StackEmptyException("栈为空!");}//2、开始删除return elem[--usedSize]; //elem[useSize]的数字没有被删掉,后续如果有push的话,会把值该赋给掉}//获取栈顶元素public int peek() {//1、判断栈不为空if(empty()) {//抛出异常!!throw new StackEmptyException("栈为空!");}//2、开始删除return elem[usedSize-1];}public boolean empty() {return usedSize == 0;}
}
(2)方法
| 方法 | 功能 |
| Stack() | 构造一个空的栈 |
| E push(E e) | 将e入栈,并返回e |
| E pop() | 将栈顶元素出栈并返回 |
| E peek() | 获取栈顶元素 |
| int size() | 获取栈中有效元素个数 |
| boolean empty() | 检测栈是否为空 |
(3)栈的应用
- 改变元素的序列
- 进栈过程中可以出栈 和 依次入栈,然后再依次出栈 对元素出栈的顺序的改变
- 将递归转化为循环
//递归方式
public void show(ListNode head) {if(head == null) {return;}if(head.next == null) {System.out.println(head.val);return;}show3(head.next);System.out.println(head.val);
}//循环方式
public void show2() {Stack<ListNode> stack = new Stack<>();ListNode cur = head;while (cur != null) {stack.push(cur);cur = cur.next;}//依次出栈while (!stack.empty()) {ListNode tmp = stack.pop();System.out.println(tmp.val);}
}
- 逆波兰表达式求值
class Solution {public int evalRPN(String[] tokens) {Deque<Integer> stack = new LinkedList<Integer>();int n = tokens.length;for (int i = 0; i < n; i++) {String token = tokens[i];if (isNumber(token)) {stack.push(Integer.parseInt(token));} else {int num2 = stack.pop();int num1 = stack.pop();switch (token) {case "+":stack.push(num1 + num2);break;case "-":stack.push(num1 - num2);break;case "*":stack.push(num1 * num2);break;case "/":stack.push(num1 / num2);break;default:}}}return stack.pop();}public boolean isNumber(String token) {return !("+".equals(token) || "-".equals(token) || "*".equals(token) || "/".equals(token));}
}
- 有效的括号
class Solution {public 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.empty()){return false;}else {char tmp = stack.peek();if (ch == ')' && tmp == '(' || ch == '}' && tmp == '{' || ch == ']' && tmp == '[') {stack.pop();}else {return false;}}}}if (!stack.empty()) {return false;}return true;}
}
- 栈的压入、弹出序列
import java.util.Stack;public class Solution {public boolean IsPopOrder(int [] pushA,int [] popA) {Stack<Integer> stack = new Stack<>();int j = 0;for (int i = 0; i < pushA.length; i++) {stack.push(pushA[i]);while (!stack.empty() && j < popA.length&& stack.peek() == popA[j]) {stack.pop();j++;}}return stack.empty();}
}
- 最小栈
class MinStack {
private Stack<Integer> stack ;private Stack<Integer> minStack ;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()) {return;}int val = stack.pop();if(val == minStack.peek()) {minStack.pop();}}//获取栈顶元素 和 最小栈没有关系public int top() {if(stack.empty()) {return -1;}return stack.peek();}//获取元素 不是删除元素public int getMin() {return minStack.peek();}}
栈、虚拟机栈、栈帧有什么区别呢?
栈是数据结构
虚拟机栈是内存
栈帧是调用方法是,在虚拟机栈开辟的一个内存
二、队列
2.1 什么是队列
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(FirstIn First Out) 入队列:进行插入操作的一端称为队尾(Tail/Rear) 出队列:进行删除操作的一端称为队头(Head/Front)
抽象场景:食堂排队买饭

2.2 队列的使用
(1)底层代码的实现
在Java中,Queue是个接口,底层是通过链表实现的
队列可以通过数组和链表实现
- 如果是双向链表,那么入队和出队,均可以达到O(1),不管从哪边进
- 如果是单链表,并且记录了最后一个节点的位置的情况下,我们可以采用从尾入队,从头出队的方式,都是O(1)。如果是从头进,从尾出,那么出队的复杂度为O(n)
(2)队列的使用
❤️创建
Queue是个接口,在实例化时必须实例化LinkedList的对象,因为LinkedList实现了Queue接口。
Queue<Integer> q = new LinkedList<>();
❤️方法
| 方法 | 功能 |
| boolean offer(E e) | 入队列 |
| E poll() | 出队列 |
| peek() | 获取队头元素 |
| int size() | 获取队列中有效元素个数 |
| boolean isEmpty() | 检测队列是否为空 |
❤️队列的模拟实现
- 顺序结构
双向链表
public class Queue {// 双向链表节点public static class ListNode{ListNode next;ListNode prev;int value;ListNode(int value){this.value = value;}}ListNode first; // 队头ListNode last; // 队尾int size = 0;// 入队列---向双向链表位置插入新节点public void offer(int e){ListNode newNode = new ListNode(e);if(first == null){first = newNode;// last = newNode;}else{last.next = newNode;newNode.prev = last;// last = newNode;}last = newNode;size++;}// 出队列---将双向链表第一个节点删除掉public int poll(){// 1. 队列为空// 2. 队列中只有一个元素----链表中只有一个节点---直接删除// 3. 队列中有多个元素---链表中有多个节点----将第一个节点删除int value = 0;if(first == null){return -1;}else if(first == last){last = null;first = null;}else{value = first.value;first = first.next;first.prev.next = null;first.prev = null;}--size;return value;}// 获取队头元素---获取链表中第一个节点的值域public int peek(){if(first == null){return -1;}return first.value;}public int size() {return size;}public boolean isEmpty(){return first == null;}
}
单向链表
public class MyQueue {static class ListNode {public int val;public ListNode next;public ListNode(int val) {this.val = val;}}public ListNode head;public ListNode last;private int usedSize;public void offer(int val) {ListNode node = new ListNode(val);if(head == null) {head = node;last = node;}else {last.next = node;last = last.next;}usedSize++;}public int getUsedSize() {return usedSize;}public int poll() {if(head == null) {return -1;}int val = -1;if(head.next == null) {val = head.val;head = null;last = null;return val;}val = head.val;head = head.next;usedSize--;return val;}public int peek() {if(head == null) {return -1;}return head.val;}}
- 循环结构
环形队列通常使用数组实现
class MyCircularQueue {private int[] elem;private int front;//队头下标private int rear;//队尾下标public MyCircularQueue(int k) {this.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() {return (rear+1) % elem.length == front;}
}
解析
- 数组下标循环:
下标最后再往后:(rear + 1) % elem.length;
下标最前再往前:(front + 1) % elem.length; - 如何区分空和满:
满: 通过添加 useSize 属性记录,当useSIze == len 的时候为满
保留一个位置,用这个位置来表示满
使用标记
空: front == rear 时为空
2.3 双端队列
- 双端队列(deque)是指允许两端都可以进行入队和出队操作的队列
- deque 是 “double ended queue” 的简称。
- 那就说明元素可以从队头出队和入队,也可以从队尾出队和入队

- Deque是一个接口,使用时必须创建相关的对象。
Deque<Integer> stack = new ArrayDeque<>(); 双端队列的线性实现,底层是数组
Deque<Integer> queue = new LinkedList<>(); 双端队列的链式实现,底层是链表
2.4 练习
一、用队列实现栈
import java.util.LinkedList;
import java.util.Queue;class MyStack {private Queue<Integer> qu1;private 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;}if(!qu1.isEmpty()) {int currentSize = qu1.size();for (int i = 0; i < currentSize-1; i++) {int x = qu1.poll();qu2.offer(x);}return qu1.poll();//最后一个数据返回}if(!qu2.isEmpty()) {int currentSize = qu2.size();for (int i = 0; i < currentSize-1; i++) {int x = qu2.poll();qu1.offer(x);}return qu2.poll();//最后一个数据返回}return -1;}//peek方法public int top() {if(empty()) {return -1;}if(!qu1.isEmpty()) {int currentSize = qu1.size();int x = -1;for (int i = 0; i < currentSize; i++) {x = qu1.poll();qu2.offer(x);}return x;//最后一个数据返回}if(!qu2.isEmpty()) {int currentSize = qu2.size();int x = -1;for (int i = 0; i < currentSize; i++) {x = qu2.poll();qu1.offer(x);}return x;//最后一个数据返回}return -1;}public boolean empty() {return qu1.isEmpty() && qu2.isEmpty();}
}
二、用栈实现队列
class MyQueue {private Stack<Integer> s1;private Stack<Integer> s2;public MyQueue() {s1 = new Stack<>();s2 = new Stack<>();}public void push(int x) {s1.push(x);}public int pop() {if(!s2.empty()) {return s2.pop();}else {while(!s1.empty()) {int val = s1.pop();s2.push(val);}return s2.pop();}}public int peek() {if(!s2.empty()) {return s2.peek();}else {while(!s1.empty()) {int val = s1.pop();s2.push(val);}return s2.peek();}}public boolean empty() {return s1.empty() && s2.empty();}
}
相关文章:
数据结构初阶(栈和队列)
文章目录 一、栈1.1 什么是栈1.2 栈的使用(1)底层代码(2)方法(3)栈的应用 二、队列2.1 什么是队列2.2 队列的使用(1)底层代码的实现(2)队列的使用 2.3 双端队…...
IDEA实用设置
1、设置全局编码统一为UTF-8 file>setting中搜索框输入file encoding修改格式为UTF-8 2、设置文字大小 file>setting中搜索框输入font修改字体大小 3、配置maven file>setting中搜索框输入maven修改maven的路径、conf文件、文件仓库 4、idea中实现Serializable提示…...
关联爆破-RSA分解
今天遇到一个RSA题,给出n和pq求分解,翻箱倒柜也没找着原来写的程序,这里重写一下。都是编程的活。 第1种情况,给出p^q 这种情况当p,q相同位相同时为0,不同时为1,爆破的时候只需要逐位判断两种情况&#x…...
Netty内存管理--内存池PoolArena
一、写在前面 到这里, 想必你已知道了Netty中的内存规格化(SizedClass), Page和SubPage级别的内存分配, 但是具体使用者不应该关心应该申请page还是subpage。而且从过去的经验来说, 申请page/subpage的数量也是个动态值, 如果申请使用完之后就释放那使用内存池的意义就不大。N…...
RabbitMQ 发布订阅模式,routing路由模式,topic模式
发布订阅模式 一个消息可以由多个消费者消费同一个消息 消费者1和2同时消费了该消息 举例 public static void main(String[] args) throws IOException, TimeoutException {//1 创建连接工厂ConnectionFactory connectionFactorynew ConnectionFactory();//2 设置rabbitmq …...
又一款可视化神器,开源了!
在互联网数据大爆炸的这几年,各类数据处理、数据可视化的需求使得 GitHub 上诞生了一大批高质量的 BI 工具。 借助这些 BI 工具,我们能够大幅提升数据分析效率、生成更高质量的项目报告,让用户通过直观的数据看到结果,减低沟通成…...
干货 | 中科院心理所考研复试经验分享
Hello,大家好! 这里是壹脑云科研圈,我是喵君姐姐~ 此时此刻,23年考研的小伙伴估计正在为复试进行准备吧,大家都准备得怎么样了呢? 今天为大家带来的就是我国顶级心理学研究结构—中科院心理所…...
Redis基础知识概述
Redis基础知识概述 文章目录 Redis基础知识概述一、Redis简介二、NoSQL技术三、Redis的高并发和快速原因四、Redis为什么是单线程的 五、单线程的优劣势1、优势2、劣势 六、Redis高并发总结七、在java中使用Redis1、添加Jedis依赖 八、Redis在Java Web中的应用1、存储缓存用的数…...
开心档之C++ 引用
C 引用 引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。 C 引用 vs 指针 引用很容易与指针混淆,它们之间有三个主要的不同:…...
后台优化主要分为哪些?工作内容及流程是什么?
什么是5G网络优化? 顾名思义就是对4G/5G无线网络进行测试,分析,优化的专业技术工作。网络优化工作的进展程度,直接关系着我们对4G/5G无线网络的使用体验。 网络优化工程师通过对现已运行的手机通话网络进行话务数据分析、现场测…...
二叉树及其遍历
文章目录 二叉树树的定义二叉树的定义遍历先序遍历中序遍历后序遍历层次遍历定义队列层次创建二叉树层次遍历 二叉树 树是一种非线性的数据结构,由若干个节点组成,节点之间存在一种父子关系,具有层次结构。二叉树是一种特殊的树结构ÿ…...
java 版本企业电子招投标采购系统源码之登录页面
信息数智化招采系统 服务框架:Spring Cloud、Spring Boot2、Mybatis、OAuth2、Security 前端架构:VUE、Uniapp、Layui、Bootstrap、H5、CSS3 涉及技术:Eureka、Config、Zuul、OAuth2、Security、OSS、Turbine、Zipkin、Feign、Monitor、…...
第五章 使用RAID与LVM磁盘阵列技术
第五章 使用RAID与LVM磁盘阵列技术 一、RAID磁盘冗余阵列 1、部署磁盘阵列 (1)、RAID0、1、5、10方案技术对比 RAID级别最少硬盘可用容量读写性能安全性特点02nn低追求最大容量和速度,任何一块盘损坏,数据全部异常。12n/2n高追…...
LeetCode 560. 和为 K 的子数组
LeetCode 560. 和为 K 的子数组 给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的连续子数组的个数 。 示例 1: 输入:nums [1,1,1], k 2 输出:2示例 2: 输入:nums [1,2,3], k 3 …...
后端要一次性返回我10万条数据
问题描述 面试官:后端一次性返回10万条数据给你,你如何处理?我:歪嘴一笑,what the f**k! 问题考察点 看似无厘头的问题,实际上考查候选人知识的广度和深度,虽然在工作中这种情况很少遇到... …...
汽车智能化「出海」红利
在高阶智能座舱中,车载导航产品作为与用户体验息息相关的模块之一,同样也进入了升级迭代周期。 基于高精度地图渲染、高精度定位算法、AR等技术的车道级导航、AR导航等产品快速上车,但同时随着人机交互多模发展以及3D沉浸式用户体验需求趋势下…...
Windows10资源管理器使用
文章目录 前言二、关联菜单操作1.分组展示2.添加选择复选框3.使用窗格模式4.功能区折叠二、“文件夹选项”对话框操作1.访问模式调整2.状态栏控制总结前言 目前Windows系统中的使用较多当属Windows10,资源管理器属于Windows系统中一个常用工具。本文总结了Windows 10 专业版下…...
【视频教程解读】Window上安装和使用autogluon V0.7
1.使用conda安装的python环境 教程使用的是极简版miniconda,由于我们的电脑中安装了anaconda,所以不需要进行进一步安装。python版本为3.9,博客里面有anaconda和python版本的对应关系。注意查看版本autogluon V0.4需要3.8或者3.9和3.10,pip版…...
10、Java继承与多态 - 内部类的概念与分类 1
10、Java继承与多态 - 内部类的概念与分类 1 什么是内部类? 如果一个事物的内部包含另一个事物,那么这就是一个内部包含另一个类,称作内部类; 例如:身体和心脏的关系,又如 -> 汽车和发动机的关系&#x…...
Java SE 面试题
文章目录 Java SE 面试题基本知识请简要介绍 Java SE。请解释 Java 的垃圾回收机制。请解释 Java 中的访问修饰符。 面向对象请解释封装、继承和多态。请解释接口和抽象类的区别。 集合框架请解释 ArrayList 和 LinkedList 的区别。请解释 Set 和 Map 接口。 异常处理请解释 Ja…...
BOTW-Save-Editor-GUI 完整技术指南:Nintendo Switch 塞尔达传说存档编辑终极方案
BOTW-Save-Editor-GUI 完整技术指南:Nintendo Switch 塞尔达传说存档编辑终极方案 【免费下载链接】BOTW-Save-Editor-GUI A Work in Progress Save Editor for BOTW 项目地址: https://gitcode.com/gh_mirrors/bo/BOTW-Save-Editor-GUI BOTW-Save-Editor-GU…...
终极指南:5分钟在Windows上配置JoyCon控制器驱动,解锁完整PC游戏体验
终极指南:5分钟在Windows上配置JoyCon控制器驱动,解锁完整PC游戏体验 【免费下载链接】JoyCon-Driver A vJoy feeder for the Nintendo Switch JoyCons and Pro Controller 项目地址: https://gitcode.com/gh_mirrors/jo/JoyCon-Driver 还在为Swi…...
如何用N_m3u8DL-RE破解加密流媒体:跨平台下载的终极指南
如何用N_m3u8DL-RE破解加密流媒体:跨平台下载的终极指南 【免费下载链接】N_m3u8DL-RE Cross-Platform, modern and powerful stream downloader for MPD/M3U8/ISM. English/简体中文/繁體中文. 项目地址: https://gitcode.com/GitHub_Trending/nm3/N_m3u8DL-RE …...
如何用 OpenAPI Generator CLI 自动生成TypeScript Angular客户端
如何用 OpenAPI Generator CLI 自动生成TypeScript Angular客户端 【免费下载链接】openapi-generator-cli A node package wrapper for https://github.com/OpenAPITools/openapi-generator 项目地址: https://gitcode.com/gh_mirrors/op/openapi-generator-cli OpenAP…...
UVa 232 Crossword Answers
题目分析 本题是一个填字游戏(Crossword Puzzle\texttt{Crossword Puzzle}Crossword Puzzle)的题目。给定一个 rcr \times crc 的网格,其中白色格子包含字母,黑色格子用 *\texttt{*}* 表示。需要按照规则对白色格子进行编号&#…...
终极解决方案:NoSleep防休眠工具让你的Windows永不休眠
终极解决方案:NoSleep防休眠工具让你的Windows永不休眠 【免费下载链接】NoSleep Lightweight Windows utility to prevent screen locking 项目地址: https://gitcode.com/gh_mirrors/nos/NoSleep 你是否曾经遇到过这样的困扰?深夜下载大型文件到…...
对比直接使用官方API,体验通过Taotoken进行多模型选型与切换的便捷性
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 对比直接使用官方API,体验通过Taotoken进行多模型选型与切换的便捷性 在实际的开发工作中,我们常常需要根据…...
【Gin】中间件练习题
路由组中间件题目描述 创建一个 /admin 路由组,给它单独加一个鉴权中间件,其他接口不受影响。规则:请求头带 token: admin123 才允许访问否则返回 401 无权限输出示例无 token:{"code":401,"msg":"无权限…...
从谐波治理到能量回馈:深入聊聊LCL滤波器在光伏逆变器和PWM整流器里的那些关键设计
LCL滤波器设计实战:从谐波抑制到能量回馈的工程权衡 在光伏逆变器和PWM整流器设计中,电流谐波治理一直是工程师面临的核心挑战。当项目要求总谐波失真率(THD)必须低于3%时,传统L滤波器往往力不从心——要么需要超大电感量导致体积膨胀&#x…...
GitLab SSH Key配置全流程复盘:从生成、复制到验证,一个命令解决‘Permission denied’
GitLab SSH Key配置全流程:从零开始到高效验证的完整指南 当你第一次在终端看到Permission denied (publickey)这个刺眼的红色错误时,那种挫败感我太熟悉了。三年前我刚接触GitLab时,花了整整一个下午才搞明白SSH Key配置的完整逻辑。本文将带…...
