数据结构:JAVA 栈和队列

news2025/1/20 19:26:55

目录

实现一个MyStack

1. push

2.pop

3.empty

4.peek

栈和链表的结合

括号匹配 

栈的压入、弹出序列

最小栈

MinStack

push ​编辑

 pop

 top

getMin

概念区分及思考:

队列

 offer(入队列)

poll(出队列)

peek(获取队头元素)

empty(判断是否为空)

 设计一个循环队列库


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

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

出栈:栈的删除操作叫做出栈。出数据在栈顶。

 

 Stack的方法都很简单,用代码写一下就懂了

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()); // 获取栈中有效元素个数---> 4
    System.out.println(s.peek()); // 获取栈顶元素---> 4
    s.pop(); // 4出栈,栈中剩余1 2 3,栈顶元素为3
    System.out.println(s.pop()); // 3出栈,栈中剩余1 2 栈顶元素为3
    if(s.empty()){
        System.out.println("栈空");
    }else{
        System.out.println(s.size());
    }
}

但是我们仅仅了解这一些还不够,我们还需要了解到Stack的源码。

 但是很奇怪,为什么这个里面没有方法呢?栈到底是顺序表还是链表呢?

extends Vector<E> 这个里面暗藏玄机。

 点进来,发现Stack没有成员变量,其实全部都是继承过来的。

所以可以知道:Stack是一个数组。

实现一个MyStack

栈的功能并不多,我们一个一个来。先创建一个栈:

public class MyStack {

    public int[] elem;
    public int usedSize;
    public static final int DEFAULT_SIZE = 10;

    public MyStack() {
        this.elem = new int[DEFAULT_SIZE];
    }
}

创建一个栈比较简单,创建一个数组就是创建一个栈,把栈的大小初始化为10,usedSize为使用的栈空间,同时也当做当前栈顶的元素的下标

 1. push

    public int push(int val) {
        if(isFull()) {                   如果栈已经满了,那么对栈空间扩容两倍
            elem = Arrays.copyOf(elem,2*elem.length);
        }
        this.elem[usedSize] = val;       下标为usedSize的元素为push的值
        usedSize++;
        return val;
    }

    public boolean isFull() {
        return usedSize == elem.length;   看看usedSize是否等于数组的长度
    }

2.pop

    public int pop() {
        if(empty()) {
            throw new MyEmptyStackException("栈为空!");
        }
        /*int ret = elem[usedSize-1];
        usedSize--;
        return ret;*/
        return elem[--usedSize];
    }

usedSize减一,这样就让原来的elem[usedSize]元素没有被指向,也就是弹出来了

public class MyEmptyStackException extends RuntimeException{
    public MyEmptyStackException() {
    }

    public MyEmptyStackException(String message) {
        super(message);
    }
}

3.empty

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

4.peek

    public int peek() {
        if(empty()) {
            throw new MyEmptyStackException("栈为空!");
        }
        return elem[usedSize-1];
    }

peek只获取栈顶的元素,并不会删除元素

同时在这里简单介绍一下中缀表达式转后缀表达式,了解即可

栈和链表的结合

逆序打印链表:

                 递归方式
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;
    }

括号匹配 

 思路:把所有的左括号用栈存放,与又括号一一匹配:

if(ch2 == '[' && ch == ']' || ch2 == '(' && ch == ')' || ch2 == '{' && ch == '}') {
    stack.pop();
}else{
    return false;
}

但我们还要考虑到栈空了或者与右括号不匹配的情况,完整代码:

    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        for(int i = 0; i < s.length();i++) {
            char ch = s.charAt(i);
            //1. 判断是不是左括号
            if(ch == '(' || ch == '[' || ch == '{') {
                stack.push(ch);
            }else {
                if(stack.empty()) {
                    //2. 遇到了右括号 但是栈为空,此时不匹配!
                    return false;
                }
                char ch2 = stack.peek();
                //3。 此时 如果满足 这里面的任何一个匹配逻辑 都是匹配的
                if(ch2 == '[' && ch == ']' || ch2 == '(' && ch == ')' || ch2 == '{' && ch == '}') {
                    stack.pop();
                }else{
                    return false;
                }
            }
        }
        //4. 当字符串遍历完成了,但是栈不为空,说明左括号还在栈当中没有匹配完成
        if(!stack.empty()) {
            return false;
        }
        return true;
    }

栈的压入、弹出序列

 思路:

运用栈来实现还是比较简单的,将pushA数组的元素按照下标入栈后,再依次与popA里面的元素相比较,只要是依次能够匹配上就说明这是匹配的出栈序列。

public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA){
        Stack<Integer> stack = new Stack<>();
        int j = 0;  j变量用来遍历popA数组
        for(int i = 0 ; i < pushA.length ; i++){
            stack.push(pushA[i]);
            while(j < popA.length && !stack.empty() && stack.peek() == popA[j]){
                stack.pop();
                j++;
            }
        }
        return stack.empty();
    }
}

最小栈

要实现minStack,仅仅用一个栈是不够的,需要两个栈:

第一个栈的元素正常进出,第二个栈仅仅进出比上一个元素小的元素。 

MinStack

class MinStack {

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

新建两个栈,第一个栈存放所有元素,第二个栈仅存放最小的元素

push 

 先让需要push的元素进入到stack1里面去,如果minStack为空那么就push到minStack里面去,如果不为空就和minStack的顶端元素相比较,只有小于等于才push到里面去,代码的实现是很简单的:

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

 pop

先让stack1pop一个元素出来,如果minStack的元素和这个元素相等,那么minStack的元素也就需要被pop

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

 top

需要注意的是,该top返回的是stack1的元素

    public int top() {
        if(!stack1.empty()){
            return stack1.peek();
        }
        return -1;
    }

getMin

获取的是最小元素,但minStack中的元素就是最小元素,也即返回minStack中的顶元素

    public int getMin() {
        if(minStack.empty()){
            return -1;
        }
        return minStack.peek();
    }

概念区分及思考:

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

栈:是一种先进后出的数据结构。

虚拟机栈:是JVM的一块内存空间。

栈帧:是在调用函数的过程中,在java虚拟机栈上开辟的一块内存。

目前实现的栈,底层是数组实现,所以也可以叫做顺序栈。请问,由单链表来实现栈是否可以?(链式栈)

但是,双链表就可以完美的实现栈的所有功能,并且时间复杂度为O(1)。

但栈用数组来完成仍然是最优解。

队列

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

java当中的队列基本上就是双向链表来实现的

 但是我们为了加大难度,我们通过单链表来实现一个队列,并且时间复杂度为O(1)。

 实现方法就是:通过给队尾加上一个tail,这样就可以让出入队时间复杂度都为O(1).

所以基础的构造就是:

    static class ListNode{
        public int val;

        public ListNode next;

        public ListNode(int val) {
            this.val = val;
        }
    }
    public ListNode head;

    public ListNode tail;

    public int Usedsize = 0;//记录队列的大小

 offer(入队列)

在这个我们自己实现的队列中,我们从尾入元素,从头出元素。也就类似于单链表里面的尾插法,从尾插入一个元素,Usedsize++。但我们仍需要注意链表如果为空需要分情况讨论。

    public void offer(int val){
        ListNode node = new ListNode(val);
        if(head == null){
            head = node;
            tail = node;
        }else{
            tail.next = node;
            tail = tail.next;
        }
        Usedsize++;
    }

poll(出队列)

只需要把head节点往后移,再返回之前的头结点的val值就能完成,但仍需要注意head为空的情况

    public int poll(){
        if(head == null){
            return -1;      这里抛异常会更好
        }
        int ret = head.val;
        head = head.next;
        if(head == null){   此时head已经是原head之后的节点了;如果head为空那么tail也需要置空
            tail = null;
        }
        Usedsize--;
        return ret;
    }

peek(获取队头元素)

返回队头的元素就行了

    public int peek(){
        if(head == null){
            return -1;
        }
        return head.val;
    }

empty(判断是否为空)

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

 设计一个循环队列库

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

在这个图中,内圈的是下标,外圈的是存放的元素。这就是一个数组,只不过不同的是首尾相连,变成了一个循环的数组。

我们采用rear表示队尾,front表示队头。

问题:rear怎么从7位置走到0位置?

   rear = (rear+1) % elem.length

我们先把基础的参数和构造方法写出来,构造数组来完成

    public int[] elem;
    public int front;
    public int rear;

    public MyCircularQueue(int k) {
        elem = new int[k];
    }

enQueue(入队):

    public boolean deQueue() {
        if(isEmpty()){
            return false;
        }
        front = (front+1) % elem.length; 
        在没有循环的时候,front++就能满足要求,有循环的时候只有这样才能完成
        return true;
    }

isEmpty(判断是否为空):

    public boolean isEmpty() {
        if(front == rear){    循环的时候,如果front和rear就代表这个循环队列为空
            return true;
        }
        return false;
    }

deQueue(出队):

    public boolean deQueue() {
        if(isEmpty()){
            return false;
        }
        front = (front+1) % elem.length;  和入队是一样的逻辑
        return true;
    }

isFull(判断是否满了):

    public boolean isFull() {
        if((rear+1) % elem.length == front){  
                    如果front和当前的rear有这样的关系就说明满了
            return true;
        }
        return false;
    }
  • Front: 从队首获取元素。如果队列为空,返回 -1 。
  • Rear: 获取队尾元素。如果队列为空,返回 -1 。
        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 index;
        }

完整代码:

class MyCircularQueue {

    public int[] elem;
    public int front;
    public int rear;

    public MyCircularQueue(int k) {
        elem = new int[k];
    }
    
    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 index;
    }
    
    public boolean isEmpty() {
        if(front == rear){
            return true;
        }
        return false;
    }
    
    public boolean isFull() {
        if((rear+1) % elem.length == front){
            return true;
        }
        return false;
    }
}

栈和队列到此就结束了,总体来说比较简单,下一章就是二叉树哦!

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

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

相关文章

Framework ——setContentView源码解读

前言 对于Android的开发者来说&#xff0c;setContentView大家再熟悉不过了&#xff0c;在我们的Activity中首先就是要用它加载我们的布局&#xff0c;但是应该有一部分人是不知道加载布局的原理&#xff0c;也包括我&#xff0c;今天就从源码的角度分析setContentView加载布局…

Java项目:JSP的电影院售票系统(含论文、任务书、中期检查表)

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 这个项目是一个基于ServletJSP的电影院售票系统&#xff0c;分为管理员和会员两种角色。 管理员权限包括&#xff1a; 修改登录密码 电影类别管…

上海亚商投顾:沪指探底回升跌0.75% 旅游板块集体大涨

上海亚商投顾前言&#xff1a;无惧大盘大跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 市场情绪三大指数早间低开&#xff0c;盘初一度集体跌超2%&#xff0c;随后震荡回升&#xff0c;午后跌幅进一步收窄。旅游板…

宝宝多大可以吃坚果 注意这几个时间节点

近年来&#xff0c;坚果的营养价值引起了公众的广泛关注。节日期间&#xff0c;许多家庭喜欢买坚果来招待客人。与其他油炸相比&#xff0c;.蜜饯零食&#xff0c;坚果真的更健康。但是&#xff0c;对于家里的宝宝来说&#xff0c;适合吃坚果吗&#xff1f;多大的宝宝能吃坚果&…

xss-labs/level-2

这一关我们先查看源代码吧 然后我们可以看得出来 输入点test存在三处输出点 依次是h2标签之间、value属性值处以及h3标签之间的长度 我们可以先尝试一下上一关输入的payload(<script>alert("xss")</script>) 界面回显如下所示 去看看源代码吧 第一处…

五、vue组件与props自定义属性

一、vue组件 1.1、什么是组件化开发 组件化开发指的是&#xff1a;根据封装的思想&#xff0c;把页面上可重用的UI结构封装为组件&#xff0c;从而方便项目的开发和维护。 1.2、vue中的组件化开发 vue是一个支持组件化开发的前端框架。 vue中规定&#xff1a;组件的后缀名是…

JavaWeb----Ajax技术

目录 Ajax技术详解 Ajax简介 Ajax 的使用 Ajax 的运行原理 XMLHttpRequest 对象 Ajax 的使用步骤 Ajax请求 ​编辑 Get/Post请求 JSON详解 JSON简介 为什么要使用 JSON JSON 格式的特征 JSON的语法规则 JOSN的数据类型 JACKSON 的使用 Jackson 简介 在项目中引入…

Scala011--Scala中的常用集合函数及操作Ⅱ

在上一篇文章中我已经接触并认识了几个常用的集合函数&#xff1a;foreach,sorted,sortBy,sortWith,flatten,map,flatMap。 接下来在这一篇文章中我将继续学习剩下的几个集合函数。 目录 一&#xff0c;filter函数 ​编辑 练习题1&#xff1a;如何过滤出大于2的奇数&#…

PyCharm+PyQT5之二第一个QT程序

Tools菜单或者右键打开external tools的 QTdesigner,如下图&#xff0c;在PyQt5中最常用的窗口有三种&#xff0c;即Main Window Widget Dialog&#xff0c; Main Window 也就是主窗口&#xff0c;他要为用户提供一个带有菜单栏、工具栏和状态栏的窗口。 Widget:通用窗口&#…

零基础入门金融风控之贷款违约预测挑战赛——简单实现

零基础入门金融风控之贷款违约预测挑战赛 赛题理解 赛题以金融风控中的个人信贷为背景&#xff0c;要求选手根据贷款申请人的数据信息预测其是否有违约的可能&#xff0c;以此判断是否通过此项贷款&#xff0c;这是一个典型的分类问题。通过这道赛题来引导大家了解金融风控中…

Linux中less和more的区别

总结下more和less的区别(less is more)&#xff1a; 1、less可以按键盘上下方向键显示上下文内容&#xff0c;more不能通过上下方向键控制显示&#xff08;但是可以用b实现向上翻页&#xff0c;空格键实现向下翻页&#xff09; 2、less不必读整个文件&#xff0c;加载速度会比…

数码视讯Q5、Q7_晶晨S905L/M/M2_nandemmc通刷_完美精简线刷固件

数码视讯Q5、Q7_晶晨S905L/M/M2_nand&emmc通刷_完美精简线刷固件 固件特点&#xff1a; 1、修改dns&#xff0c;三网通用&#xff1b; 2、开放原厂固件屏蔽的市场安装和u盘安装apk&#xff1b; 3、无开机广告&#xff0c;无系统更新&#xff0c;不在被强制升级&#xf…

一道有趣的最长子序列问题

一道有趣的最长子序列问题 – 潘登同学的金融经济学笔记 文章目录一道有趣的最长子序列问题 -- 潘登同学的金融经济学笔记来源求解递推公式算法实现来源 前几天在刷视频的时候&#xff0c;发现了这样一道题 所谓子序列就是一个序列 ai1,ai2,⋯,aina_{i1},a_{i2},\cdots,a_{in}…

【5w字】SpringBoot源码分析

Spring Boot源码分析 文章目录Spring Boot源码分析启动类分析SpringBootConfigurationEnableAutoConfigurationComponentScanSpringBoot启动流程1. 准备SpringApplication获取当前 web 应用类型设置初始化器设置监听器推导主应用程序类2. Spring Boot的运行创建计时器并启动设置…

开源工具 | ASV-Subtools更新:runtime模块重磅发布

ASV-Subtools更新 - runtime模块重磅发布 - ASV-Subtools是厦门大学智能语音实验室&#xff08;XMUSPEECH&#xff09;于2020年6月推出的一套高效、易于扩展的声纹识别开源工具&#xff0c;该工具是基于Kaldi与Pytorch开发的&#xff0c;充分结合了Kaldi 在语音信号和后端处理…

Fe3+-多巴胺修饰Pluronic的多功能性水凝胶/多巴胺修饰聚丙烯微孔膜表面的研究

下面整理了Fe3-多巴胺修饰Pluronic的多功能性水凝胶/多巴胺修饰聚丙烯微孔膜表面&#xff0c;和小编一起来看&#xff01; Fe3-多巴胺修饰Pluronic多功能性水凝胶 利用对硝基苯基氯酸甲酯作为聚合物Pluronic F127端羟基活化剂,与盐酸多巴胺直接反应制备末端多巴胺修饰的Pluron…

【web课程设计网页规划与设计】基于HTML+CSS+JavaScript火车票网上预订系统网站(4个页面)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

Part4_场景_第52章 场景描述第53章 柏林I:BVG场景第54章 柏林II:CEMDAP-Matsim-Cadyts方案

第52章 场景概述 本书的最后一部分总结了MATSIM场景,如图52.1中的地图所示,并在http://matsim.org/scolutions中列出。 尽管存在基于免费和公共数据的真实场景,如圣地亚哥或科特布斯场景(第84或66章),但由于数据隐私问题,许多场景是不公开的。 然而,了解适用于情景创建…

【STM32备忘录】二、FSMC做LCD控制器,使用stm32cube配置示例

文章目录1. FSMC框图2. 配置示例(1) cube配置(2) 代码参考*疑问1. FSMC框图 如果屏幕接口8位数据宽度访问&#xff0c;地址线FSMC_A[25:0], 一共26位&#xff0c;一个块22667108864Byte64MB&#xff0c;如果屏幕接口16位数据宽度访问&#xff0c;地址线FSMC_A[25:0], 一共26位&…

远程访问数据库,快解析助力疫情防控远程办公

当前&#xff0c;国内疫情形势不容乐观&#xff0c;企业的经营发展再一次面临巨大挑战。虽然“远程办公”早已不是新鲜词&#xff0c;但依然有大量企业没有做好随时切换到远程办公的准备。如遇疫情风险&#xff0c;企业运营很容易陷入瘫痪。 企业一般在内网部署服务器、视频监…