Java栈和队列

🐵本文章将对栈相关知识进行讲解


一、什么是栈

栈是一种特殊的线性表,向栈中放入元素的次序是由栈底到栈顶依次放入,被称为入栈压栈,从栈中出元素时只能从栈顶出,被称为出栈。即栈要求元素“先进后出”

下面给一道经典的例题:

 1. 若进栈序列为 1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈序列是()
A: 1,4,3,2         B: 2,3,4,1         C: 3,1,4,2         D: 3,4,2,1

答案:C ;1,2,3依次入栈,之后3出栈后只能让2出栈后才能让1出栈

二、Stack类

Stack是Java提供的类,其中包含了可以对栈进行一系列操作的常见方法

栈的常用方法如下:

E push(E e)  //将e入栈,并返回e

E pop()  //将栈顶元素出栈并返回

E peek()  //获取栈顶元素,栈顶元素不出栈

int size()  //获取栈中有效元素个数

boolean empty()  //检测栈是否为空

三、模拟实现栈

有两种实现栈的方法,一是用数组,二是用链表

在Stack类中方法的底层就是用的数组实现的,所以这里也用数组来模拟实现栈

public class MyStack {
    public int[] elem;
    public int usedSize; //记录栈中存放元素的个数
    public static final int DEFAULT_CAPACITY = 5; //栈的默认容量

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


    /*实现栈中的方法*/
    ...

}
public void push(int val)

元素入栈,也就是在数组的usedSize位置放入元素,入栈后usedSize++;和顺序表一样也要考虑数组是否已经满了,如果满了则要扩容

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

    public boolean isFull() { //判断数组是否满了的方法
        return usedSize == elem.length;
    }

public int pop()

将栈顶元素出栈并返回,当然如果栈为空的话则要抛出异常

    public int pop() {
        if (empty()) {
            throw new EmptyStackException("栈为空");
        }

        usedSize--;
        return elem[usedSize];
    }
    public boolean empty() { //判断栈是否为空的方法
        return usedSize == 0;
    }

public int peek()

只获取栈顶元素,不出栈

    public int peek() {
        if (isEmpty()) {
            throw new EmptyStackException("栈为空");
        }
        int val = elem[--usedSize]; //获取栈顶元素
        usedSize++; //由于元素不出栈,所以要usedSize++
        return val;
    }

用链表实现栈,如果用单链表的话,由于入栈操作要保证时间复杂度为O(1),所以必须用头插法,同理,出栈操作必须用头删法;但对于双向链表来说,由于其有指向最后一个节点的引用last和指向前驱节点的引用prev,所以更加灵活,入栈操作可以头插也可以尾插,出栈操作可以头删也可以尾删

在LinkedList类中有实现栈中的方法

四、相关概念

栈、虚拟机栈、函数栈帧;这里对这几个概念进行一个浅浅的区分

栈是一种数据结构

虚拟机栈是JVM的一块内存

函数栈帧是调用方法时为方法开辟的一块内存

五、什么是队列

队列也是一种特殊的线性表,元素只能从队列的队尾进入,被称为入队,从队列中出元素时只能从队头出,被称为出队。即队列要求元素“先进先出”

六、Queue接口

Queue是Java提供的接口,其中包含了对队列进行一系列操作的常见方法

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

在Queue接口中有以下几个方法:

boolean add(E e);
boolean offer(E e); //入队

E remove();
E poll();  //出队

E element();
E peek(); //获取队头元素

下面分别讨论一下1.addoffer的区别 2.remove poll的区别 3.element peek的区别

1.add 和 offer

这两个方法都是将一个元素插入到一个队列的队尾,也就是入队,区别在于:对于一个有限队列,在队列容量满的情况下,add方法会抛出IllegalStateException的异常,而offer方法则会返回false

2.remove 和 poll

这两个方法都是将队头元素删除并返回,也就是出队,区别在于:对于一个空队列,remove方法会抛出异常,poll方法会返回null

3.element 和 peek

这两个方法都是获取队头元素,区别在于:对于一个空队列,element方法会抛出异常,peek方法会返回null

对于队列进行操作的常用的方法如下:

boolean offer(E e)  //入队列

E poll()  //出队列

E peek()  //获取队头元素

int size()  //获取队列中有效元素个数

boolean isEmpty()  //检测队列是否为空

七、模拟实现队列

Queue接口中的方法在底层是用链表实现的,这里我们使用双链表模拟实现队列,创建一个MyQueue类:

public class MyQueue {

    static class ListNode{
        public int val;
        public ListNode next;
        public ListNode prev;

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

    public ListNode head;
    public ListNode last;

    /*以下为要模拟实现的方法*/
    ...

}

boolean offer(E e)

元素只能从队尾入队,对于双链表,入队操作可以看作头插也可以看作尾插,这里使用尾插法实现offer方法

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

boolean poll()

元素只能从队头出队,对于双链表,出队操作可以看作头删也可以看作尾删,由于前面入队采取的是尾插,所以这里采用头删法入队

当队列为空时,返回-1(这里假定队列中的元素都是int型);然后进行正常的头删

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

        head.next.prev = null;
        head = head.next;
        return val;
    }

这里有一种特殊的情况要考虑,就是链表中只有一个节点时上述代码会抛出空指针异常,所以这种情况要单独处理,完整代码如下:

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

        head.next.prev = null;
        head = head.next;
        return val;
    }

E peek()

获取队头元素,只需要将head.val 返回即可

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

八、循环队列

8.1 为什么要有循环队列

对于一个数组,如下:

为了减小空间的浪费,于是就有了循环队列

8.2 什么是循环队列

循环队列又称为环形队列,循环队列中已出队的元素的位置可以作为入队元素的位置,减小了空间的浪费

由上图可以看出循环队列是一个有限队列,那么该如何判断队列是否为满的,这里介绍三种方法:

1.计数器:定义一个count,每入队或出队一个元素都进行计数,当count等于队列的长度时说明队列已满

2.使用标记        

3.空出一个位置:空出的位置不放元素,当(rear + 1) % elem.length == front 时说明队列已满

8.3 模拟实现循环队列

这里用数组实现循环队列

public class MyCircularQueue {

    public int[] elem;
    public int front; //队头
    public int rear; //队尾

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

    /*以下是要模拟实现的方法*/
    ...


}

boolean enQueue(int value) //入队

首先判断一下队列是否已满,如果为满则直接返回false,这里使用上述讲过的第三种方法来判断队列是否已满,rear指向队尾元素的下一个位置,在队尾插入元素只需要elem[rear] = value即可,插入元素后要将rear指向队尾元素的下一个位置,这里不能简单的让rear = rear + 1;因为当rear = elem.length时如果让rear + 1就会导致数组越界

    public boolean enQueue(int value) {
        if (isFull()) {
            return false;
        }
        elem[rear] = value;
        rear = (rear + 1) % elem.length;
        return true;
    }

    public boolean isFull() {
        return (rear + 1) % elem.length == front;
    }

boolean deQueue() //出队

对于数组来说,出队操作只需让front指向其下一个位置即可,这里同样不能让front = front + 1,而是front = (front + 1) % elem.length

    public boolean deQueue() {
        if (isEmpty()) {
            return false;
        }

        front = (front + 1) % elem.length;
        return true;
    }
    public boolean isEmpty() {
        return rear == front;
    }

int Front() //获取队头元素
    public int Front() {
        if (isEmpty()) {
            return -1;
        }
        
        return elem[front];
    }

int Rear() //获取队尾元素

由于指向队尾元素的下一个位置,所以队尾元素应该是elem[rear - 1],但是当rear为0时就变成了elem[-1],无法获取队尾元素,所以分为两种情况:int index = rear == 0 ? elem.length - 1 : rear - 1

    public int Rear() {
        if (isEmpty()) {
            return -1;
        }

        int index = (rear == 0) ? elem.length : rear - 1;
        return elem[index];
    }


🙉本篇文章到此结束,接下来会对二叉树相关知识进行讲解

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

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

相关文章

Vivado-IP核

Vivado-IP核 主程序 timescale 1ns / 1ps ////module ip_clk_wiz(input sys_clk,input sys_rst_n,output clk_out1,output clk_out2,output clk_out3,output clk_out4,output locked);clk_wiz_0 instance_name(// Clock out ports.clk_out1(clk_out1), // output clk_out…

ywtool login guard命令

一.登录防护功能介绍 登录防护功能主要检查系统日志/var/log/secure,查看系统有没有被暴力登录。登录防护默认是检测3分钟内登录系统失败15次(次数可修改)后,视其为有攻击性,拉黑此IP(centos7通过系统文件阻止IP,centos8/9通过防火墙阻止IP)。此脚本只针对SSH访问,…

layui

基于复杂结构的自定义模版相关介绍 我这里的接口给的格式数据 我这里搜索往返时候要显示成这样的 layui.use([table,form], function(){ var table layui.table; var form layui.form;// 渲染表格 table.render({ elem: #test-table-reload,toolbar: #toolbarDemo, …

【Python基础】seaborn 使用指南(超详细!)

文章目录 seaborn1 seaborn简介1.1 主要特征:1.2 seaborn主要内容 2 seaborn基本设置2.1 图表大小:context2.2 设置风格2.3 设置字体与支持中文2.4 设置临时风格2.5 设置调色板2.6 set方法 3 调色板3.1 分类色板(qualitative)3.2 …

3.0 Hadoop 概念

本章着重介绍 Hadoop 中的概念和组成部分,属于理论章节。如果你比较着急可以跳过。但作者不建议跳过,因为它与后面的章节息息相关。 Hadoop 整体设计 Hadoop 框架是用于计算机集群大数据处理的框架,所以它必须是一个可以部署在多台计算机上…

chisel RegInit/UInt/U

val reg RegInit(0.U(8.W)) //ok val reg RegInit(0.UInt(8.W)) //errU 使用在数字 . 后边50.U UInt 使用在IO(new Bundle val a Input(UInt(8.W)) 或者 def counter(max:UInt, a1:UInt) package emptyimport chisel3._ import chisel3.util._class MyCounter extends …

Java技术栈 —— Hive与HBase

Java技术栈 —— Hive与HBase 一、 什么是Hive与HBase二、如何使用Hive与HBase?2.1 Hive2.1.1 安装2.1.2 使用2.1.2.1 使用前准备2.1.2.2 开始使用hive 2.2 HBase2.2.1 安装2.2.2 使用 三、Apache基金会 一、 什么是Hive与HBase 见参考文章。 一、参考文章或视频链…

神经网络激活函数到底是什么?

激活函数 其实不是很难啦,归结一下就是大概这样几个分类,详情请参考【神经网络】大白话直观理解!_哔哩哔哩_bilibili神经网络就是干这个事的~ 如果队伍不长,一个ykxb就可以了,如果 如果 队伍太长 就加一个激活函数也…

C语言函数递归详解

递归是什么&#xff1f; 递归&#xff0c;顾名思义&#xff0c;就是递推和回归。 递归是一种解决问题的方法&#xff0c;在C语言中&#xff0c;递归就是函数自己调用自己。 #include <stdio.h> int main() {printf("hehe\n");main();//main函数中⼜调⽤了main…

C++ 调用lua 脚本

需求&#xff1a; 使用Qt/C 调用 lua 脚本 扩展原有功能。 步骤&#xff1a; 1&#xff0c;工程中引入 头文件&#xff0c;库文件。lua二进制下载地址&#xff08;Lua Binaries&#xff09; 2&#xff0c; 调用脚本内函数。 这里调用lua 脚本中的process函数&#xff0c;并…

FFMPEG推流到B站直播

0、参考 ffmpeg安装参考小弟另外的一个博客&#xff1a;FFmpeg和rtsp服务器搭建视频直播流服务-CSDN博客推流参考&#xff1a;用ffmpeg 做24小时推流直播_哔哩哔哩_bilibili 一、获取b站直播码 点击开始直播后&#xff0c;会出现以下的画面 二、ffmpeg进行直播推流 ffmpeg -r…

const

当我们在c语言中碰到这么一种情况&#xff1a;我们现在有一个变量&#xff0c; 这个变量呢&#xff0c;我们指向访问它&#xff0c; 但不会修改它。但是又担心在后续的代码中不小心将它修改&#xff0c; 这种情况该怎么做呢&#xff1f;这种情况下可以使用const. 被const修饰的…

全套电气自动化样例图纸分享,使用SuperWorks自动化版免费设计软件!

今天给大家分享一套完备的电气自动化样例图纸&#xff0c;结构准确、内容清晰&#xff0c;适合初学者入门操作练习。 整套图纸包含图纸目录、原理图、端子列表、连接列表、元件列表、接线图&#xff0c;具有较高的参考价值&#xff0c;请大家点击自行下载文件&#xff01; 1e8…

springcloud-gateway升级版本allowedOrigins要改allowedOriginPatterns

前言 报错: java.lang.IllegalArgumentException: When allowCredentials is true,allowedOrigins cannot contain the special value "*"since that cannot be set on the "Access-Control-Allow-Origin"response header. To allow credentials to a se…

如何让虚拟机拥有愉快网络环境,vmware,ubuntu,centos

博客原文 文章目录 前言拥有愉快网络环境步骤:测试网关连接 Ubuntu修改 http 与 sock 代理地址修改 /etc/resolv.conf配置 apt 使用代理测试连接 Centos设置代理地址修改 NetworkManager最后重启网卡&#xff1a;测试代理 前言 相信计算机专业的同学在学习 linux 时, 一定会被无…

L1-027 出租-java

输入样例&#xff1a; 18013820100输出样例&#xff1a; int[] arr new int[]{8,3,2,1,0}; int[] index new int[]{3,0,4,3,1,0,2,4,3,4,4}; java import java.util.*;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);St…

无线远程多层立体土壤墒情监测仪

TH-GTS03无线远程多层立体土壤墒情监测仪是一款用于监测土壤水分状况的智能设备&#xff0c;可以帮助农民和农业科技人员实时了解土壤的含水量和土壤温度&#xff0c;科学地进行农田管理和合理安排灌溉、施肥等农事活动&#xff0c;提高作物产量和品质。 该仪器采用了先进的传感…

时隔3年 | 微软 | Windows Server 2025 重磅发布

最新功能 以下是微软产品团队正在努力的方向&#xff1a; Windows Server 2025 为所有人提供的热补丁下一代 AD 活动目录和 SMB数据与存储Hyper-V 和人工智能还有更多… Ignite 发布视频 Windows Server 2025 Ignite Video 介绍 Windows Server 2022 正式发布日期是2021年…

Go 中 struct tag 如何用?基于它实现字段级别的访问控制

嗨&#xff0c;大家好&#xff01;本文是系列文章 Go 小技巧第十篇&#xff0c;系列文章查看&#xff1a;Go 语言小技巧。 在 Go 中&#xff0c;结构体主要是用于定义复杂数据类型。而 struct tag 则是附加在 struct 字段后的字符串&#xff0c;提供了一种方式来存储关于字段的…

Oracle12c之Sqlplus命令行窗口基本使用

Oracle12c之Sqlplus命令行窗口基本使用 文章目录 Oracle12c之Sqlplus命令行窗口基本使用1. 连接1. 超级用户2. 普通用户1. 创建普通用2. 连接 2. 修改用户连接数1. 查看默认连接最多用户数1. PL/SQL developer中查看2. Sqlplus中查看 2. 查看目前已经连接的用户数3. 修改用户连…